Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 6x 10x 6x 10x 10x 10x 10x 10x 18x 10x 18x 10x 18x 10x 10x 10x 10x 10x 1x 63x 10x 10x 63x | <template>
<div
ref="rail"
class="action-rail d-flex"
>
<template v-for="(action, index) of visible">
<div class="visible-item">
<slot
name="visible"
:action="action"
:index="index"
></slot>
</div>
</template>
<v-menu
v-if="showMenu"
v-bind="menuProps"
v-on="menuOn"
>
<template #activator="{ props }">
<v-btn v-bind="mergeProps(props, menuActivatorProps)" />
</template>
<v-list>
<template v-for="(action, index) of hidden">
<div class="hidden-item">
<slot
name="hidden"
:action="action"
:index="index"
></slot>
</div>
</template>
</v-list>
</v-menu>
</div>
</template>
<script setup lang="ts">
import { computed, mergeProps, onBeforeUnmount, onMounted, ref } from 'vue';
type Action<T = Record<string, unknown>> = {
width: number;
props: Record<string, any>;
} & T;
interface Props {
actions?: Action[];
menuProps?: Record<string, unknown>;
menuOn?: Record<string, unknown>;
menuActivatorProps?: Record<string, unknown>;
}
const props = withDefaults(defineProps<Props>(), {
actions: () => [],
menuProps: () => ({}),
menuOn: () => ({}),
menuActivatorProps: () => ({}),
});
const rail = ref<HTMLDivElement>();
const splitIndex = ref<number>(0);
/* v8 ignore start -- @preserve */
const observer = new ResizeObserver((entries) => {
if (entries.length > 0) {
const railWidth = entries[0].contentRect.width;
calculateSplitIndex(railWidth);
}
});
/* v8 ignore stop -- @preserve */
const visible = computed(() => {
return props.actions.slice(0, splitIndex.value);
});
const hidden = computed(() => {
return props.actions.slice(splitIndex.value);
});
const showMenu = computed(() => {
return hidden.value.length > 0;
});
onMounted(() => {
observer.observe(rail.value!);
const rect = rail.value!.getBoundingClientRect();
calculateSplitIndex(rect.width);
});
onBeforeUnmount(() => {
observer.disconnect();
});
/* v8 ignore start -- @preserve */
function calculateSplitIndex(railWidth: number): void {
const widths = props.actions.map((action) => action.width);
let sum = 48;
for (const [idx, width] of widths.entries()) {
Iif (sum + width < railWidth) {
sum += width;
splitIndex.value = idx + 1;
}
}
}
/* v8 ignore stop -- @preserve */
</script>
<style lang="scss">
.action-rail {
min-width: 0;
}
</style>
|