Add collapsable breadcrumbs component [SCI-11184]

This commit is contained in:
Anton 2024-10-14 14:38:50 +02:00
parent 8e0f0e27a7
commit adcc401a79
7 changed files with 132 additions and 7 deletions

View file

@ -8,7 +8,7 @@ class UserNotificationsController < ApplicationController
notifications = load_notifications
case params[:tab]
when 'all'
when 'read'
notifications = notifications.where.not(read_at: nil)
when 'unread'
notifications = notifications.where(read_at: nil)

View file

@ -0,0 +1,22 @@
import { createApp } from 'vue/dist/vue.esm-bundler.js';
import Breadcrumbs from '../../../vue/shared/breadcrumbs.vue';
import { mountWithTurbolinks } from '../helpers/turbolinks.js';
const app = createApp({
computed: {
breadcrumbs() {
return [
{ name: 'Home', url: '/' },
{ name: 'Very very very long name ', url: '' },
{ name: 'Data', url: '' },
{ name: 'Very very very very very very very very very very long name ', url: '' },
{ name: 'Very very very very very very very long name ', url: '' },
{ name: 'Very very very very very long name ', url: '' },
{ name: 'Very very very very long name ', url: '' }
];
}
}
});
app.component('Breadcrumbs', Breadcrumbs);
app.config.globalProperties.i18n = window.I18n;
mountWithTurbolinks(app, '#breadcrumbs');

View file

@ -16,24 +16,23 @@
<div v-html="notification.attributes.message" class="sci-navigation--notificaitons-flyout-notification-message"></div>
</a>
<div v-if="notification.attributes.breadcrumbs" class="flex items-center flex-wrap gap-0.5">
<template v-for="(breadcrumb, index) in notification.attributes.breadcrumbs" :key="index">
<div class="flex items-center gap-0.5">
<i v-if="index > 0" class="sn-icon sn-icon-right"></i>
<a :href="breadcrumb.url" :title="breadcrumb.name" class="truncate max-w-[20ch] inline-block">{{ breadcrumb.name }}</a>
</div>
</template>
<Breadcrumbs :breadcrumbs="notification.attributes.breadcrumbs" />
</div>
</div>
</template>
<script>
import axios from '../../../packs/custom_axios.js';
import Breadcrumbs from '../../shared/breadcrumbs.vue';
export default {
name: 'NotificationItem',
props: {
notification: Object
},
components: {
Breadcrumbs
},
computed: {
lastBreadcrumbUrl() {
if (!this.notification.attributes.breadcrumbs) {

View file

@ -0,0 +1,93 @@
<template>
<div ref="container" class="w-full flex items-center flex-wrap gap-0.5">
<template v-if="!allVisible">
<div class="flex items-center gap-0.5">
<a :href="breadcrumbs[0].url" :title="breadcrumbs[0].name" class="max-w-[200px]">
<StringWithEllipsis class="w-full" :text="breadcrumbs[0].name"></StringWithEllipsis>
</a>
</div>
<div class="flex items-center gap-0.5">
<i class="sn-icon sn-icon-right text-sn-grey"></i>
<GeneralDropdown>
<template v-slot:field>
<a>...</a>
</template>
<template v-slot:flyout>
<div class="max-w-[600px]">
<div v-for="(breadcrumb, index) in hiddenBreadcrumbs" :key="index" class="p-2 hover:bg-sn-super-light-grey cursor-pointer">
<a :href="breadcrumb.url" :title="breadcrumb.name" class="max-w-[200px] hover:no-underline">
<StringWithEllipsis class="w-full" :text="breadcrumb.name"></StringWithEllipsis>
</a>
</div>
</div>
</template>
</GeneralDropdown>
<i class="sn-icon sn-icon-right text-sn-grey"></i>
</div>
</template>
<div v-for="(breadcrumb, index) in visibleBreadcrumbs" :key="index" class="flex items-center gap-0.5">
<i v-if="index > 0" class="sn-icon sn-icon-right text-sn-grey"></i>
<a :href="breadcrumb.url" :title="breadcrumb.name" class="max-w-[200px]">
<StringWithEllipsis class="w-full" :text="breadcrumb.name"></StringWithEllipsis>
</a>
</div>
</div>
</template>
<script>
import StringWithEllipsis from './string_with_ellipsis.vue';
import GeneralDropdown from './general_dropdown.vue';
export default {
name: 'Breadcrumbs',
props: {
breadcrumbs: {
type: Array,
required: true
}
},
components: {
StringWithEllipsis,
GeneralDropdown
},
data() {
return {
containerSize: 0,
breadcrumbSize: 200
};
},
computed: {
breadcrumbsVisibleCount() {
const size = Math.floor(this.containerSize / this.breadcrumbSize);
if (size < 2) {
return 2;
}
return size;
},
allVisible() {
return this.breadcrumbs.length <= this.breadcrumbsVisibleCount;
},
hiddenBreadcrumbs() {
return this.breadcrumbs.slice(1, this.breadcrumbs.length - this.breadcrumbsVisibleCount + 1);
},
visibleBreadcrumbs() {
if (this.allVisible) {
return this.breadcrumbs;
}
return this.breadcrumbs.slice(this.breadcrumbs.length - this.breadcrumbsVisibleCount + 1);
}
},
mounted() {
this.setContainerSize();
window.addEventListener('resize', this.setContainerSize);
},
beforeDestroy() {
window.removeEventListener('resize', this.setContainerSize);
},
methods: {
setContainerSize() {
this.containerSize = this.$refs.container.offsetWidth;
}
}
};
</script>;

View file

@ -0,0 +1,8 @@
<div>
<h1>breadcrumbs</h1>
<div id="breadcrumbs" class="flex items-center gap-4">
<Breadcrumbs :breadcrumbs="breadcrumbs">
</div>
</div>
<%= javascript_include_tag 'vue_design_system_breadcrumbs' %>

View file

@ -14,6 +14,8 @@
<%= render partial: 'select' %>
<%= render partial: 'breadcrumbs' %>
<%= render partial: 'modals' %>
<%= render partial: 'icons', locals: {icons_list: icons_list} %>

View file

@ -54,6 +54,7 @@ const entryList = {
vue_experiments_list: './app/javascript/packs/vue/experiments_list.js',
vue_my_modules_list: './app/javascript/packs/vue/my_modules_list.js',
vue_design_system_select: './app/javascript/packs/vue/design_system/select.js',
vue_design_system_breadcrumbs: './app/javascript/packs/vue/design_system/breadcrumbs.js',
vue_protocols_list: './app/javascript/packs/vue/protocols_list.js',
vue_repositories_table: './app/javascript/packs/vue/repositories_table.js',
vue_import_repository_modal: './app/javascript/packs/vue/import_repository_modal.js',