(core) Aggregate and reverse lookups

Summary:
Reverse and Aggregation lookup.
Aggregation lookup works when table have a reference list column. It allow to list value of any fields of a referenced values, or to make some basic operation on them (sum, average, count)
Reverse lookup works as reverse one, but it allow do to the same operations on all rows that have reference to given row

Test Plan: Manual so far.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D4083
This commit is contained in:
Jakub Serafin
2023-10-20 13:05:29 +02:00
parent 74485f412d
commit 91f7606ae6
4 changed files with 390 additions and 37 deletions

View File

@@ -56,8 +56,10 @@ export interface SearchableMenuOptions {
export interface SearchableMenuItem {
cleanText: string;
label: string;
action: (item: HTMLElement) => void;
builder?: () => Element;
label?: string;
action?: (item: HTMLElement) => void;
args?: DomElementArg[];
}
@@ -86,19 +88,29 @@ export function searchableMenu(
const cleanSearchValue = value.trim().toLowerCase();
return dom.forEach(menuItems, (item) => {
if (!item.cleanText.includes(cleanSearchValue)) { return null; }
return menuItem(item.action, item.label, ...(item.args ?? []));
if (item.label && item.action) {
return menuItem(item.action, item.label, ...(item.args || []));
} else if (item.builder) {
return item.builder();
} else {
throw new Error('Invalid menu item');
}
});
}),
];
}
// TODO Weasel doesn't allow other options for submenus, but probably should.
export type ISubMenuOptions =
weasel.ISubMenuOptions &
weasel.IPopupOptions &
{allowNothingSelected?: boolean};
/**
* Menu item with submenu
*/
export function menuItemSubmenu(
submenu: weasel.MenuCreateFunc,
options: ISubMenuOptions,
@@ -108,7 +120,8 @@ export function menuItemSubmenu(
submenu,
{
...defaults,
expandIcon: () => icon('Expand'),
expandIcon: () => cssExpandIcon('Expand'),
menuCssClass: `${cssSubMenuElem.className} ${defaults.menuCssClass}`,
...options,
},
dom.cls(cssMenuItemSubmenu.className),
@@ -116,6 +129,41 @@ export function menuItemSubmenu(
);
}
/**
* Subheader as a menu item.
*/
export function menuSubHeaderMenu(
submenu: weasel.MenuCreateFunc,
options: ISubMenuOptions,
...args: DomElementArg[]
): Element {
return menuItemSubmenu(
submenu,
{
...options,
},
menuSubHeader.cls(''),
cssPointer.cls(''),
...args,
);
}
export const cssEllipsisLabel = styled('div', `
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`);
export const cssExpandIcon = styled(icon, `
position: absolute;
right: 4px;
`);
const cssSubMenuElem = styled('div', `
white-space: nowrap;
min-width: 200px;
`);
export const cssMenuElem = styled('div', `
font-family: ${vars.fontFamily};
font-size: ${vars.mediumFontSize};
@@ -483,11 +531,15 @@ export const menuSubHeader = styled('div', `
color: ${theme.menuSubheaderFg};
font-size: ${vars.xsmallFontSize};
text-transform: uppercase;
font-weight: ${vars.bigControlTextWeight};
font-weight: ${vars.headerControlTextWeight};
padding: 8px 24px 8px 24px;
cursor: default;
`);
export const cssPointer = styled('div', `
cursor: pointer;
`);
export const menuText = styled('div', `
display: flex;
align-items: center;
@@ -503,6 +555,12 @@ export const menuItem = styled(weasel.menuItem, menuItemStyle);
export const menuItemLink = styled(weasel.menuItemLink, menuItemStyle);
// when element name is, to long, it will be trimmed with ellipsis ("...")
export function menuItemTrimmed(
action: (item: HTMLElement, ev: Event) => void, label: string, ...args: DomElementArg[]) {
return menuItem(action, cssEllipsisLabel(label), ...args);
}
/**
* A version of menuItem which runs the action on next tick, allowing the menu to close even when
@@ -706,6 +764,7 @@ const cssUpgradeTextButton = styled(textButton, `
`);
const cssMenuItemSubmenu = styled('div', `
position: relative;
color: ${theme.menuItemFg};
--icon-color: ${theme.menuItemFg};
.${weasel.cssMenuItem.className}-sel {