mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-10-28 05:20:43 +00:00
fix(editor): 优化导航更多菜单宽度与溢出分割线
不传 popoverWidth 时按内容撑开,并将溢出项中的分割线改为横向且去掉首尾无意义分割线。
This commit is contained in:
parent
7f0e78bf6a
commit
8ac1a38601
@ -62,13 +62,14 @@ const props = withDefaults(
|
||||
width?: number;
|
||||
/** 子元素之间的间距,需与 SCSS gap 保持一致 */
|
||||
gap?: number;
|
||||
/** Popover 内容宽度 */
|
||||
popoverWidth?: number;
|
||||
/**
|
||||
* Popover 内容宽度。不传时按内容撑开,避免下拉框超出菜单。
|
||||
*/
|
||||
popoverWidth?: number | string;
|
||||
}>(),
|
||||
{
|
||||
items: () => [],
|
||||
gap: 3,
|
||||
popoverWidth: 180,
|
||||
},
|
||||
);
|
||||
|
||||
@ -110,7 +111,18 @@ const containerWidth = ref(0);
|
||||
const hiddenIndexSet = ref<Set<number>>(new Set());
|
||||
|
||||
const hasOverflow = computed(() => hiddenIndexSet.value.size > 0);
|
||||
const overflowItems = computed(() => props.items.filter((_, index) => hiddenIndexSet.value.has(index)));
|
||||
// 更多菜单是纵向列表:分割线改成横线;收进去的首尾分割线没有分组意义,直接去掉。
|
||||
const overflowItems = computed(() => {
|
||||
const items = props.items
|
||||
.filter((_, index) => hiddenIndexSet.value.has(index))
|
||||
.map((item) => (item.type === 'divider' ? { ...item, direction: 'horizontal' as const } : item));
|
||||
|
||||
let start = 0;
|
||||
let end = items.length;
|
||||
while (start < end && items[start].type === 'divider') start += 1;
|
||||
while (end > start && items[end - 1].type === 'divider') end -= 1;
|
||||
return items.slice(start, end);
|
||||
});
|
||||
|
||||
const measureAndCompute = () => {
|
||||
if (!columnEl.value) return;
|
||||
|
||||
@ -120,7 +120,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.m-editor-nav-menu-popover {
|
||||
.tmagic-design-popper.m-editor-nav-menu-popover {
|
||||
// 不固定宽度,按内容撑开,避免下拉框超出菜单;content-box 让 padding 不挤占内容宽度。
|
||||
width: max-content;
|
||||
min-width: 180px;
|
||||
box-sizing: content-box;
|
||||
|
||||
.m-editor-nav-menu-overflow-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -139,8 +144,30 @@
|
||||
}
|
||||
|
||||
&.divider {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 4px 12px;
|
||||
cursor: default;
|
||||
box-sizing: border-box;
|
||||
|
||||
// 更多菜单是纵向列表,分割线必须盖过组件默认的竖线样式
|
||||
.tmagic-design-divider,
|
||||
.el-divider,
|
||||
.t-divider {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--el-border-color, #dcdfe6);
|
||||
border-left: 0;
|
||||
position: static;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
|
||||
@ -21,7 +21,8 @@ vi.mock('@editor/components/ToolButton.vue', () => ({
|
||||
'button',
|
||||
{
|
||||
ref: rootEl,
|
||||
class: ['tool-btn', (props.data as any)?.className],
|
||||
class: ['tool-btn', (props.data as any)?.className, (props.data as any)?.type],
|
||||
'data-direction': (props.data as any)?.direction || '',
|
||||
},
|
||||
(props.data as any)?.text || '',
|
||||
);
|
||||
@ -119,6 +120,15 @@ describe('NavMenuColumn', () => {
|
||||
expect(moreWrapper.classes()).toContain('m-editor-nav-menu-more-wrapper-hidden');
|
||||
});
|
||||
|
||||
test('未指定 popoverWidth 时不给更多菜单固定宽度', () => {
|
||||
const wrapper = mount(NavMenuColumn as any, {
|
||||
props: { columnKey: 'left', items: [{ type: 'button', text: 'A' }] },
|
||||
});
|
||||
const popover = wrapper.findComponent({ name: 'TMagicPopover' });
|
||||
expect(popover.exists()).toBe(true);
|
||||
expect(popover.props('width')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('点击 more 引用元素切换 popover 显示状态', async () => {
|
||||
const wrapper = mount(NavMenuColumn as any, {
|
||||
props: { columnKey: 'left', items: [{ type: 'button', text: 'A' }] },
|
||||
@ -344,6 +354,127 @@ describe('NavMenuColumn', () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('更多菜单中的分割线改为横向,工具栏中的分割线保持竖向', async () => {
|
||||
const items = [
|
||||
{ type: 'button', text: 'A' },
|
||||
{ type: 'button', text: 'B' },
|
||||
{ type: 'divider', className: 'sep' },
|
||||
{ type: 'button', text: 'C' },
|
||||
];
|
||||
|
||||
const wrapper = mount(NavMenuColumn as any, {
|
||||
props: { columnKey: 'right', items, width: 80 },
|
||||
attachTo: document.body,
|
||||
});
|
||||
|
||||
const columnEl = wrapper.find('.m-editor-nav-menu-column').element as HTMLElement;
|
||||
Object.defineProperty(columnEl, 'clientWidth', { configurable: true, get: () => 80 });
|
||||
|
||||
const moreEl = wrapper.find('.m-editor-nav-menu-more-wrapper').element as HTMLElement;
|
||||
moreEl.getBoundingClientRect = () => ({
|
||||
width: 30,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
|
||||
wrapper.findAll('.tool-btn').forEach((b) => {
|
||||
const el = b.element as HTMLElement;
|
||||
el.getBoundingClientRect = () => ({
|
||||
width: 40,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
});
|
||||
|
||||
roCallbacks.forEach((cb) => cb());
|
||||
await flushRaf();
|
||||
await flushRaf();
|
||||
|
||||
const overflowDividers = wrapper.findAll('.m-editor-nav-menu-overflow-list .sep');
|
||||
expect(overflowDividers.length).toBeGreaterThan(0);
|
||||
overflowDividers.forEach((divider) => {
|
||||
expect(divider.attributes('data-direction')).toBe('horizontal');
|
||||
});
|
||||
|
||||
wrapper.findAll('.m-editor-nav-menu-column > .sep').forEach((divider) => {
|
||||
expect(divider.attributes('data-direction')).toBe('');
|
||||
});
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('更多菜单首尾的分割线会被去掉,中间的分割线保留', async () => {
|
||||
const items = [
|
||||
{ type: 'button', text: 'A' },
|
||||
{ type: 'divider', className: 'lead' },
|
||||
{ type: 'button', text: 'B' },
|
||||
{ type: 'divider', className: 'mid' },
|
||||
{ type: 'button', text: 'C' },
|
||||
{ type: 'divider', className: 'trail' },
|
||||
];
|
||||
|
||||
const wrapper = mount(NavMenuColumn as any, {
|
||||
props: { columnKey: 'right', items, width: 80 },
|
||||
attachTo: document.body,
|
||||
});
|
||||
|
||||
const columnEl = wrapper.find('.m-editor-nav-menu-column').element as HTMLElement;
|
||||
Object.defineProperty(columnEl, 'clientWidth', { configurable: true, get: () => 80 });
|
||||
|
||||
const moreEl = wrapper.find('.m-editor-nav-menu-more-wrapper').element as HTMLElement;
|
||||
moreEl.getBoundingClientRect = () => ({
|
||||
width: 30,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
|
||||
wrapper.findAll('.tool-btn').forEach((b) => {
|
||||
const el = b.element as HTMLElement;
|
||||
el.getBoundingClientRect = () => ({
|
||||
width: 40,
|
||||
height: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
});
|
||||
|
||||
roCallbacks.forEach((cb) => cb());
|
||||
await flushRaf();
|
||||
await flushRaf();
|
||||
|
||||
const overflow = wrapper.find('.m-editor-nav-menu-overflow-list');
|
||||
expect(overflow.find('.lead').exists()).toBe(false);
|
||||
expect(overflow.find('.trail').exists()).toBe(false);
|
||||
expect(
|
||||
overflow.findAll('.tool-btn').map((button) => button.text() || button.classes().find((name) => name === 'mid')),
|
||||
).toEqual(['B', 'mid', 'C']);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('卸载时清理 ResizeObserver', () => {
|
||||
const wrapper = mount(NavMenuColumn as any, {
|
||||
props: { columnKey: 'left', items: [{ type: 'button', text: 'A' }] },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user