<div id="app">
<div ref="container" class="container">
<!-- Action buttons -->
<button v-for="action in actionButtons" :key="action" @click="doAction(action)">
{{ action }}
</button>
<!-- Menu button -->
<button ref="menuButton" v-show="menuActions.length" @click.stop="toggleMenu">
…
</button>
<!-- Action menu items -->
<div ref="menu" v-show="isMenuOpen" class="menu">
<button v-for="action in menuActions" :key="action" @click="doAction(action)">
{{ action }}
</button>
</div>
</div>
<p>Container Size</p>
<div class="change-size">
<button @click="changeSize('100%')">100%</button>
<button @click="changeSize('1400px')">1400px</button>
<button @click="changeSize('400px')">400px</button>
<button @click="changeSize('200px')">200px</button>
</div>
</div>
.container {
display: flex;
width: 100%; /* This can be changed via the buttons. */
padding: 8px;
align-items: center;
justify-content: center;
overflow: hidden;
background: #ffe2e4;
button {
border: 0;
cursor: pointer;
}
& > button {
margin: 0.25rem;
padding: 12px 20px;
font-size: 16px;
border-radius: 0.25rem;
background: #ed3c3c;
color: #fff;
&:hover {
background: #b01a1a;
}
&:active {
background: #e86f6f;
}
}
.menu {
display: flex;
flex-direction: column;
background: #b01a1a;
border-radius: 8px;
overflow: hidden;
& > button {
font-size: 15px;
padding: 10px 24px;
border-radius: 0;
background: transparent;
text-align: left;
color: #fff;
&:hover {
background: #ed3c3c;
}
&:active {
background: #e86f6f;
}
}
}
}
/* The rest is just setup */
html,
body,
#app {
height: 100%;
}
#app {
display: flex;
flex-direction: column;
align-items: center;
font-family: sans-serif;
padding: 16px;
}
p {
margin-top: 32px;
text-align: center;
color: #4c11f7;
text-transform: uppercase;
font-weight: 600;
font-size: 14px;
letter-spacing: 0.5px;
}
.change-size {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
button {
width: 100px;
margin: 2px;
padding: 12px 20px;
border: 0;
border-radius: 4px;
font-family: inherit;
font-size: 14px;
font-weight: 600;
letter-spacing: 0.5px;
background: #4c11f7;
color: #fff;
cursor: pointer;
&:hover {
background: #3000e0;
}
&:active {
background: #5c00ff;
}
}
}
View Compiled
let resizeObserver;
let popperInstance;
new Vue({
el: "#app",
data() {
return {
actions: ["Edit", "Save", "Copy", "Rename", "Share", "Delete"],
isMenuOpen: false,
menuActions: [] // Actions that should be shown in the menu
};
},
computed: {
actionButtons() {
// Actions that should be shown as buttons outside the menu
return this.actions.filter(
(action) => !this.menuActions.includes(action)
);
}
},
mounted() {
// Attach ResizeObserver to the container
resizeObserver = new ResizeObserver(this.onResize);
resizeObserver.observe(this.$refs.container);
// Close the menu on any click
document.addEventListener("click", this.closeMenu);
},
beforeDestroy() {
// Clean up the observer and event listener
resizeObserver.disconnect();
document.removeEventListener("click", this.closeMenu);
},
methods: {
onResize() {
requestAnimationFrame(async () => {
// Place all buttons outside the menu
if (this.menuActions.length) {
this.menuActions = [];
await this.$nextTick();
}
const isOverflowing = () =>
this.$refs.container.scrollWidth > this.$refs.container.offsetWidth;
// Move buttons into the menu until the container is no longer overflowing
while (isOverflowing() && this.actionButtons.length) {
const lastActionButton = this.actionButtons[
this.actionButtons.length - 1
];
this.menuActions.unshift(lastActionButton);
await this.$nextTick();
}
});
},
toggleMenu() {
if (!this.isMenuOpen) {
this.isMenuOpen = true;
popperInstance = Popper.createPopper(
this.$refs.menuButton,
this.$refs.menu,
{
placement: "bottom-end",
modifiers: [
{ name: "flip" },
{ name: "offset", options: { offset: [0, 1] } }
]
}
);
} else this.closeMenu();
},
closeMenu() {
if (!this.isMenuOpen) return;
this.isMenuOpen = false;
popperInstance.destroy();
popperInstance = undefined;
},
doAction(action) {
console.log(action + " clicked!");
},
changeSize(size) {
this.$refs.container.style.width = size;
}
}
});
View Compiled
This Pen doesn't use any external CSS resources.