ditch bootstrap-vue dropdown

This commit is contained in:
Nazar Kanaev 2021-03-28 20:56:51 +01:00
parent 36bc84d99a
commit b3ba912566
3 changed files with 118 additions and 78 deletions

View File

@ -35,76 +35,73 @@
<span class="icon">{% inline "assorted.svg" %}</span> <span class="icon">{% inline "assorted.svg" %}</span>
</button> </button>
<div class="flex-grow-1"></div> <div class="flex-grow-1"></div>
<b-dropdown <dropdown class="settings-dropdown" toggle-class="btn btn-link toolbar-item px-2" ref="menuDropdown">
right no-caret lazy variant="link" <template v-slot:button>
class="settings-dropdown"
toggle-class="toolbar-item px-2"
ref="menuDropdown">
<template v-slot:button-content class="toolbar-item">
<span class="icon">{% inline "more-horizontal.svg" %}</span> <span class="icon">{% inline "more-horizontal.svg" %}</span>
</template> </template>
<b-dropdown-item-button @click="showSettings('create')">
<button class="dropdown-item" @click="showSettings('create')">
<span class="icon mr-1">{% inline "plus.svg" %}</span> <span class="icon mr-1">{% inline "plus.svg" %}</span>
New Feed New Feed
</b-dropdown-item-button> </button>
<b-dropdown-item-button @click.stop="showSettings('manage')"> <button class="dropdown-item" @click="showSettings('manage')">
<span class="icon mr-1">{% inline "list.svg" %}</span> <span class="icon mr-1">{% inline "list.svg" %}</span>
Manage Feeds Manage Feeds
</b-dropdown-item-button> </button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-item-button @click.stop="fetchAllFeeds()"> <button class="dropdown-item" @click="fetchAllFeeds()">
<span class="icon mr-1">{% inline "rotate-cw.svg" %}</span> <span class="icon mr-1">{% inline "rotate-cw.svg" %}</span>
Refresh Feeds Refresh Feeds
</b-dropdown-item-button> </button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-header>Refresh</b-dropdown-header> <header class="dropdown-header">Refresh</header>
<b-dropdown-item-button @click.stop="refreshRate = min" v-for="min in [0, 60]"> <button class="dropdown-item" @click.stop="refreshRate = min" v-for="min in [0, 60]">
<span class="icon mr-1" :class="{invisible: refreshRate != min}">{% inline "check.svg" %}</span> <span class="icon mr-1" :class="{invisible: refreshRate != min}">{% inline "check.svg" %}</span>
<span v-if="min == 0">Manually</span> <span v-if="min == 0">Manually</span>
<span v-if="min == 60">Every hour</span> <span v-if="min == 60">Every hour</span>
</b-dropdown-item-button> </button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-header>Sort by</b-dropdown-header> <header class="dropdown-header">Sort by</header>
<b-dropdown-item-button @click.stop="itemSortNewestFirst=true"> <button class="dropdown-item" @click.stop="itemSortNewestFirst=true">
<span class="icon mr-1" :class="{invisible: !itemSortNewestFirst}">{% inline "check.svg" %}</span> <span class="icon mr-1" :class="{invisible: !itemSortNewestFirst}">{% inline "check.svg" %}</span>
Newest First Newest First
</b-dropdown-item-button> </button>
<b-dropdown-item-button @click="itemSortNewestFirst=false"> <button class="dropdown-item" @click.stop="itemSortNewestFirst=false">
<span class="icon mr-1" :class="{invisible: itemSortNewestFirst}">{% inline "check.svg" %}</span> <span class="icon mr-1" :class="{invisible: itemSortNewestFirst}">{% inline "check.svg" %}</span>
Oldest First Oldest First
</b-dropdown-item-button> </button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-header>Subscriptions</b-dropdown-header> <header class="dropdown-header">Subscriptions</header>
<b-dropdown-form id="opml-import-form" enctype="multipart/form-data"> <form id="opml-import-form" enctype="multipart/form-data" tabindex="-1">
<input type="file" <input type="file"
id="opml-import" id="opml-import"
@change="importOPML" @change="importOPML"
name="opml" name="opml"
style="opacity: 0; width: 1px; height: 0; position: absolute; z-index: -1;"> style="opacity: 0; width: 1px; height: 0; position: absolute; z-index: -1;">
<label class="dropdown-item mb-0 cursor-pointer" for="opml-import"> <label class="dropdown-item mb-0 cursor-pointer" for="opml-import" @click.stop="">
<span class="icon mr-1">{% inline "download.svg" %}</span> <span class="icon mr-1">{% inline "download.svg" %}</span>
Import Import
</label> </label>
</b-dropdown-form> </form>
<b-dropdown-item href="./opml/export"> <a class="dropdown-item" href="./opml/export">
<span class="icon mr-1">{% inline "upload.svg" %}</span> <span class="icon mr-1">{% inline "upload.svg" %}</span>
Export Export
</b-dropdown-item> </a>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-item-button @click="showSettings('shortcuts')"> <button class="dropdown-item" @click="showSettings('shortcuts')">
<span class="icon mr-1">{% inline "help-circle.svg" %}</span> <span class="icon mr-1">{% inline "help-circle.svg" %}</span>
Shortcuts Shortcuts
</b-dropdown-item-button> </button>
<b-dropdown-divider v-if="authenticated"></b-dropdown-divider> <div class="dropdown-divider" v-if="authenticated"></div>
<b-dropdown-item-button v-if="authenticated" @click="logout()"> <button class="dropdown-item" v-if="authenticated" @click="logout()">
<span class="icon mr-1">{% inline "log-out.svg" %}</span> <span class="icon mr-1">{% inline "log-out.svg" %}</span>
Log out Log out
</b-dropdown-item-button> </button>
</b-dropdown> </dropdown>
</div> </div>
<div id="feed-list-scroll" class="p-2 overflow-auto border-top flex-grow-1"> <div id="feed-list-scroll" class="p-2 overflow-auto border-top flex-grow-1">
<label class="selectgroup"> <label class="selectgroup">
@ -325,18 +322,15 @@
{{ folder.title }} {{ folder.title }}
</div> </div>
<div class="flex-shrink-0" v-if="folder.id"> <div class="flex-shrink-0" v-if="folder.id">
<b-dropdown right no-caret lazy variant="link" class="settings-dropdown" toggle-class="text-decoration-none"> <dropdown class="settings-dropdown" toggle-class="btn btn-link text-decoration-none">
<template v-slot:button-content> <template v-slot:button>
<span class="icon">{% inline "more-vertical.svg" %}</span> <span class="icon">{% inline "more-vertical.svg" %}</span>
</template> </template>
<b-dropdown-header>{{ folder.title }}</b-dropdown-header> <header class="dropdown-header">{{ folder.title }}</header>
<b-dropdown-item @click.prevent="renameFolder(folder)">Rename</b-dropdown-item> <button class="dropdown-item" @click="renameFolder(folder)">Rename</button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-item class="dropdown-danger" <button class="dropdown-item text-danger" @click="deleteFolder(folder)">Delete</button>
@click.prevent="deleteFolder(folder)"> </dropdown>
Delete
</b-dropdown-item>
</b-dropdown>
</div> </div>
</div> </div>
<div v-for="feed in folder.feeds" <div v-for="feed in folder.feeds"
@ -354,36 +348,36 @@
{% inline "alert-circle.svg" %} {% inline "alert-circle.svg" %}
</span> </span>
<div class="flex-shrink-0"> <div class="flex-shrink-0">
<b-dropdown right no-caret lazy variant="link" class="settings-dropdown" toggle-class="text-decoration-none"> <dropdown class="settings-dropdown" toggle-class="btn btn-link text-decoration-none">
<template v-slot:button-content> <template v-slot:button>
<span class="icon">{% inline "more-vertical.svg" %}</span> <span class="icon">{% inline "more-vertical.svg" %}</span>
</template> </template>
<b-dropdown-header>{{ feed.title }}</b-dropdown-header> <header class="dropdown-header">{{ feed.title }}</header>
<b-dropdown-item :href="feed.link" target="_blank" v-if="feed.link">Visit Website</b-dropdown-item> <a class="dropdown-item" :href="feed.link" target="_blank" v-if="feed.link">Visit Website</a>
<b-dropdown-divider v-if="feed.link"></b-dropdown-divider> <div class="dropdown-divider" v-if="feed.link"></div>
<b-dropdown-item @click.prevent="renameFeed(feed)">Rename</b-dropdown-item> <button class="dropdown-item" @click="renameFeed(feed)">Rename</button>
<b-dropdown-divider v-if="folders.length"></b-dropdown-divider> <div class="dropdown-divider" v-if="folders.length"></div>
<b-dropdown-header v-if="folders.length">Move to...</b-dropdown-header> <header class="dropdown-header" v-if="folders.length">Move to...</header>
<b-dropdown-item @click="moveFeed(feed, null)" v-if="feed.folder_id"> <button class="dropdown-item" @click="moveFeed(feed, null)" v-if="feed.folder_id">
--- ---
</b-dropdown-item> </button>
<b-dropdown-item-button <button class="dropdown-item"
v-if="folder.id != feed.folder_id" v-if="folder.id != feed.folder_id"
v-for="folder in folders" v-for="folder in folders"
@click="moveFeed(feed, folder)"> @click="moveFeed(feed, folder)">
<span class="icon mr-1">{% inline "folder.svg" %}</span> <span class="icon mr-1">{% inline "folder.svg" %}</span>
{{ folder.title }} {{ folder.title }}
</b-dropdown-item-button> </button>
<b-dropdown-item-button @click="moveFeedToNewFolder(feed)"> <button class="dropdown-item" @click="moveFeedToNewFolder(feed)">
<span class="text-muted icon mr-1">{% inline "plus.svg" %}</span> <span class="text-muted icon mr-1">{% inline "plus.svg" %}</span>
<span class="text-muted">New Folder</span> <span class="text-muted">New Folder</span>
</b-dropdown-item-button> </button>
<b-dropdown-divider></b-dropdown-divider> <div class="dropdown-divider"></div>
<b-dropdown-item class="dropdown-danger" <button class="dropdown-item text-danger"
@click.prevent="deleteFeed(feed)"> @click.prevent="deleteFeed(feed)">
Delete Delete
</b-dropdown-item> </button>
</b-dropdown> </dropdown>
</div> </div>
</div> </div>
</div> </div>

View File

@ -83,6 +83,55 @@ Vue.component('drag', {
}, },
}) })
Vue.component('dropdown', {
props: ['class', 'toggle-class', 'ref'],
data: function() {
return {open: false, openClass: ''}
},
template: `
<div class="dropdown" :class="$attrs.class">
<button ref="btn" @click="toggle" :class="btnToggleClass"
class="dropdown-toggle-no-caret dropdown-toggle"><slot name="button"></slot></button>
<div ref="menu" class="dropdown-menu" :class="openClass"><slot v-if="open"></slot></div>
</div>
`,
computed: {
btnToggleClass: function() {
var c = this.$props.toggleClass || ''
c += ' dropdown-toggle dropdown-toggle-no-caret'
c += this.open ? ' open' : ''
return c.trim()
}
},
methods: {
toggle: function(e) {
if (this.open) {
this.hide()
} else {
e.stopPropagation()
this.show()
}
},
show: function(e) {
this.open = true
this.openClass = ' show'
this.$refs.menu.style.right = '0'
this.$refs.menu.style.left = 'auto'
this.$refs.menu.style.top = (this.$refs.btn.offsetHeight) + 'px'
document.addEventListener('click', this.clickHandler)
},
hide: function() {
this.open = false
this.openClass = ''
document.removeEventListener('click', this.clickHandler)
},
clickHandler: function(e) {
if (e.target.closest('.dropdown') == null) this.hide()
if (e.target.closest('.dropdown-item') != null) this.hide()
}
},
})
function dateRepr(d) { function dateRepr(d) {
var sec = (new Date().getTime() - d.getTime()) / 1000 var sec = (new Date().getTime() - d.getTime()) / 1000
var neg = sec < 0 var neg = sec < 0

View File

@ -53,17 +53,18 @@ select.form-control:not([multiple]):not([size]) {
padding-right: 0; padding-right: 0;
} }
.dropdown-menu { .settings-dropdown .dropdown-menu {
padding: 0; padding: 0;
box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.07); box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.07);
overflow: hidden; overflow: hidden;
} }
.dropdown-item, .dropdown-header { .settings-dropdown .dropdown-item,
.settings-dropdown .dropdown-header {
padding: .375rem 1rem; padding: .375rem 1rem;
} }
.dropdown-divider { .settings-dropdown .dropdown-divider {
margin: 0; margin: 0;
} }
@ -71,16 +72,16 @@ select.form-control:not([multiple]):not([size]) {
outline: none; outline: none;
} }
.settings-dropdown .dropdown-item {
cursor: pointer;
}
.settings-dropdown .dropdown-item:focus { .settings-dropdown .dropdown-item:focus {
outline: none; outline: none;
} }
.settings-dropdown.large .dropdown-item { .settings-dropdown form:focus {
padding: .5rem 1rem; outline: none;
}
.dropdown-danger .dropdown-item {
color: #dc3545!important;
} }
.modal-backdrop { .modal-backdrop {
@ -92,10 +93,6 @@ select.form-control:not([multiple]):not([size]) {
transform: none !important; transform: none !important;
} }
.b-dropdown-form:focus {
outline: none;
}
.popover:focus { .popover:focus {
outline: none; outline: none;
} }