Skip to content
This repository was archived by the owner on Aug 28, 2024. It is now read-only.

Commit 94db8ce

Browse files
authored
Add hover menus and fix ProjectCard issue with omitted properties (#166)
* Adds hover menus * Allow ProjectCard props to be omitted * Bump version
1 parent ca2336a commit 94db8ce

5 files changed

Lines changed: 179 additions & 63 deletions

File tree

docs/components/popout-menu.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,24 @@
7474
</template>
7575
</PopoutMenu>
7676
```
77+
78+
# Popout Menu on hover
79+
<DemoContainer>
80+
<PopoutMenu class="btn btn-dropdown-animation" position="bottom" direction="right" allow-hover>
81+
Hover me! <DropdownIcon />
82+
<template #menu>
83+
Menu contents!
84+
Menu contents!
85+
Menu contents!
86+
</template>
87+
</PopoutMenu>
88+
</DemoContainer>
89+
90+
```vue
91+
<PopoutMenu class="btn" position="bottom" direction="right" allow-hover>
92+
Hover me!
93+
<template #menu>
94+
Menu contents!
95+
</template>
96+
</PopoutMenu>
97+
```

lib/components/base/OverflowMenu.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
:disabled="disabled"
66
:position="position"
77
:direction="direction"
8+
:allow-hover="allowHover"
89
>
910
<slot></slot>
1011
<template #menu>
@@ -66,6 +67,10 @@ defineProps({
6667
type: String,
6768
default: 'left',
6869
},
70+
allowHover: {
71+
type: Boolean,
72+
default: false,
73+
},
6974
})
7075
defineOptions({
7176
inheritAttrs: false,

lib/components/base/PopoutMenu.vue

Lines changed: 143 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<template>
2-
<div ref="dropdown" class="popup-container" tabindex="-1" :aria-expanded="dropdownVisible">
2+
<div
3+
ref="dropdown"
4+
class="popup-container"
5+
tabindex="-1"
6+
:aria-expanded="dropdownVisible"
7+
:class="{ 'allow-hover': allowHover }"
8+
>
39
<button
410
v-bind="$attrs"
511
ref="dropdownButton"
@@ -9,6 +15,11 @@
915
>
1016
<slot></slot>
1117
</button>
18+
<div
19+
v-if="allowHover"
20+
class="hover-bounding-box"
21+
:class="`position-${position}-${direction}`"
22+
></div>
1223
<div
1324
class="popup-menu"
1425
:class="`position-${position}-${direction} ${dropdownVisible ? 'visible' : ''}`"
@@ -34,6 +45,10 @@ const props = defineProps({
3445
type: String,
3546
default: 'left',
3647
},
48+
allowHover: {
49+
type: Boolean,
50+
default: false,
51+
},
3752
})
3853
defineOptions({
3954
inheritAttrs: false,
@@ -90,6 +105,133 @@ onBeforeUnmount(() => {
90105
.popup-container {
91106
position: relative;
92107
108+
&.allow-hover .hover-bounding-box {
109+
position: absolute;
110+
inset: 0;
111+
border-radius: var(--radius-md);
112+
z-index: 1;
113+
114+
// Debug properties
115+
// background-color: red;
116+
// opacity: 0.1;
117+
118+
// Q: Why is this here and also below?
119+
// A: For the few browsers that do not yet support :has(), this at least
120+
// makes it mostly functional.
121+
&:hover {
122+
&.position-bottom-left,
123+
&.position-bottom-right {
124+
bottom: -1rem;
125+
}
126+
127+
&.position-top-left,
128+
&.position-top-right {
129+
top: -1rem;
130+
}
131+
132+
&.position-left-up,
133+
&.position-left-down {
134+
left: -1rem;
135+
}
136+
137+
&.position-right-up,
138+
&.position-right-down {
139+
right: -1rem;
140+
}
141+
}
142+
}
143+
144+
&:not(.allow-hover) .popup-menu:not(.visible):not(:focus-within),
145+
&.allow-hover:not(:has(.hover-bounding-box:hover)):not(:has(.popup-menu:hover))
146+
.popup-menu:not(.visible):not(:focus-within) {
147+
pointer-events: none;
148+
149+
*,
150+
::before,
151+
::after {
152+
pointer-events: none;
153+
}
154+
}
155+
156+
&.allow-hover:has(.hover-bounding-box:hover),
157+
&.allow-hover:has(.popup-menu:hover),
158+
:has(.popup-menu.visible),
159+
:has(.popup-menu:focus-within) {
160+
:deep(.btn-dropdown-animation svg:last-child) {
161+
transform: rotate(180deg);
162+
}
163+
164+
.hover-bounding-box {
165+
&.position-bottom-left,
166+
&.position-bottom-right {
167+
bottom: -1rem;
168+
}
169+
170+
&.position-top-left,
171+
&.position-top-right {
172+
top: -1rem;
173+
}
174+
175+
&.position-left-up,
176+
&.position-left-down {
177+
left: -1rem;
178+
}
179+
180+
&.position-right-up,
181+
&.position-right-down {
182+
right: -1rem;
183+
}
184+
}
185+
}
186+
187+
&.allow-hover .hover-bounding-box:hover + .popup-menu,
188+
&.allow-hover .popup-menu:hover,
189+
.popup-menu.visible,
190+
.popup-menu:focus-within {
191+
opacity: 1;
192+
scale: 1;
193+
194+
&.position-bottom-left {
195+
top: calc(100% + var(--gap-sm));
196+
right: 0;
197+
}
198+
199+
&.position-bottom-right {
200+
top: calc(100% + var(--gap-sm));
201+
left: 0;
202+
}
203+
204+
&.position-top-left {
205+
bottom: calc(100% + var(--gap-sm));
206+
right: 0;
207+
}
208+
209+
&.position-top-right {
210+
bottom: calc(100% + var(--gap-sm));
211+
left: 0;
212+
}
213+
214+
&.position-left-up {
215+
bottom: 0rem;
216+
right: calc(100% + var(--gap-sm));
217+
}
218+
219+
&.position-left-down {
220+
top: 0rem;
221+
right: calc(100% + var(--gap-sm));
222+
}
223+
224+
&.position-right-up {
225+
bottom: 0rem;
226+
left: calc(100% + var(--gap-sm));
227+
}
228+
229+
&.position-right-down {
230+
top: 0rem;
231+
left: calc(100% + var(--gap-sm));
232+
}
233+
}
234+
93235
.popup-menu {
94236
--_animation-offset: -1rem;
95237
position: absolute;
@@ -149,62 +291,6 @@ onBeforeUnmount(() => {
149291
left: calc(100% + var(--gap-sm) - 1rem);
150292
}
151293
152-
&:not(.visible):not(:focus-within) {
153-
pointer-events: none;
154-
155-
*,
156-
::before,
157-
::after {
158-
pointer-events: none;
159-
}
160-
}
161-
162-
&.visible,
163-
&:focus-within {
164-
opacity: 1;
165-
scale: 1;
166-
167-
&.position-bottom-left {
168-
top: calc(100% + var(--gap-sm));
169-
right: 0;
170-
}
171-
172-
&.position-bottom-right {
173-
top: calc(100% + var(--gap-sm));
174-
left: 0;
175-
}
176-
177-
&.position-top-left {
178-
bottom: calc(100% + var(--gap-sm));
179-
right: 0;
180-
}
181-
182-
&.position-top-right {
183-
bottom: calc(100% + var(--gap-sm));
184-
left: 0;
185-
}
186-
187-
&.position-left-up {
188-
bottom: 0rem;
189-
right: calc(100% + var(--gap-sm));
190-
}
191-
192-
&.position-left-down {
193-
top: 0rem;
194-
right: calc(100% + var(--gap-sm));
195-
}
196-
197-
&.position-right-up {
198-
bottom: 0rem;
199-
left: calc(100% + var(--gap-sm));
200-
}
201-
202-
&.position-right-down {
203-
top: 0rem;
204-
left: calc(100% + var(--gap-sm));
205-
}
206-
}
207-
208294
.btn {
209295
white-space: nowrap;
210296
}

lib/components/base/ProjectCard.vue

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,23 @@
2222
<div class="summary">{{ description }}</div>
2323
<div class="stats">
2424
<span
25+
v-if="downloads"
2526
v-tooltip="formatMessage(messages.downloads, { count: formatNumber(downloads, false) })"
2627
class="stat consumes-click"
2728
>
2829
<span class="label"><DownloadIcon /></span>
2930
<span class="value">{{ formatNumber(downloads) }}</span>
3031
</span>
3132
<span
33+
v-if="follows"
3234
v-tooltip="formatMessage(messages.followers, { count: formatNumber(follows, false) })"
3335
class="stat consumes-click"
3436
>
3537
<span class="label"><HeartIcon /></span>
3638
<span class="value">{{ formatNumber(follows) }}</span>
3739
</span>
3840
<span
39-
v-if="showUpdatedDate"
41+
v-if="showUpdatedDate && updatedAt"
4042
v-tooltip="
4143
formatMessage(messages.updated, {
4244
date: dayjs(updatedAt).format('MMMM D, YYYY [at] h:mm A'),
@@ -48,7 +50,7 @@
4850
<span class="value">{{ fromNow(updatedAt) }}</span>
4951
</span>
5052
<span
51-
v-else
53+
v-else-if="createdAt"
5254
v-tooltip="
5355
formatMessage(messages.published, {
5456
date: dayjs(createdAt).format('MMMM D, YYYY [at] h:mm A'),
@@ -60,8 +62,10 @@
6062
<span class="value">{{ fromNow(createdAt) }}</span>
6163
</span>
6264
<div class="tags">
63-
<TagIcon />
64-
<Categories :type="type" :categories="categories" />
65+
<template v-if="categories && categories.length > 0">
66+
<TagIcon />
67+
<Categories :type="type" :categories="categories" />
68+
</template>
6569
</div>
6670
</div>
6771
</article>
@@ -145,7 +149,7 @@ defineProps({
145149
},
146150
createdAt: {
147151
type: String,
148-
default: '0000-00-00',
152+
default: null,
149153
},
150154
updatedAt: {
151155
type: String,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "omorphia",
33
"type": "module",
4-
"version": "0.9.2",
4+
"version": "0.9.3",
55
"files": [
66
"dist",
77
"locales"

0 commit comments

Comments
 (0)