@3mo/data-grid
Version:
A data grid web component
279 lines (257 loc) • 9.36 kB
JavaScript
import { __decorate } from "tslib";
import { bind, Component, component, css, html, join, property, style, unsafeCSS } from '@a11d/lit';
import { tooltip } from '@3mo/tooltip';
import { Localizer } from '@3mo/localization';
import { ResizeController } from '@3mo/resize-observer';
import { DataGridColumn } from './DataGridColumn.js';
import { DataGridSortingStrategy } from './DataGridSortingController.js';
import { ReorderabilityState } from './DataGridReorderabilityController.js';
Localizer.dictionaries.add('de', {
'Sorting': 'Sortierung',
'Sort descending': 'Absteigend sortieren',
'Sort ascending': 'Aufsteigend sortieren',
'Stickiness': 'Fixierung',
'Stick to start': 'Anfang fixieren',
'Stick to both': 'Beide fixieren',
'Stick to end': 'Ende fixieren',
'Hide': 'Ausblenden',
});
let DataGridColumnHeader = class DataGridColumnHeader extends Component {
constructor() {
super(...arguments);
this.menuOpen = false;
this.resizeController = new ResizeController(this, {
callback: () => requestIdleCallback(() => this.column.widthInPixels = this.getBoundingClientRect().width)
});
}
static get styles() {
return css `
:host {
display: flex;
position: relative;
padding: 0 var(--mo-data-grid-cell-padding);
transition: background 0.1s;
anchor-name: --mo-data-grid-column-header;
}
:host([data-reorderability=${unsafeCSS(ReorderabilityState.Dragging)}]) {
opacity: 0.5;
}
:host([data-reorderability=${unsafeCSS(ReorderabilityState.DropBefore)}]) {
border-inline-start: 3px solid var(--mo-color-accent);
}
:host([data-reorderability=${unsafeCSS(ReorderabilityState.DropAfter)}]) {
border-inline-end: 3px solid var(--mo-color-accent);
}
:host(:hover) {
background: color-mix(in srgb, var(--mo-color-surface), var(--mo-color-gray) 8%);
}
${DataGridColumn.stickyStyles}
mo-data-grid-header-separator {
z-index: 5;
}
:host([data-sticky]) mo-data-grid-header-separator {
z-index: 7;
}
#container {
transition: margin-inline-end 0.1s;
}
#content {
display: inline-block;
overflow: hidden !important;
color: var(--mo-color-foreground);
font-weight: 500;
line-height: var(--mo-data-grid-header-height);
white-space: nowrap;
text-overflow: ellipsis;
user-select: none;
width: 100%;
}
#sort {
position: relative;
&[data-preview] {
display: none;
}
mo-icon-button {
transition: 0.1s;
font-size: 20px;
color: var(--mo-color-accent);
}
span {
position: absolute;
inset-inline-end: 0;
inset-block-end: 0;
color: var(--mo-color-accent);
border-radius: 50%;
width: fit-content;
user-select: none;
font-size: 0.8rem;
aspect-ratio: 1 / 1;
display: flex;
align-items: center;
justify-content: center;
}
}
:host(:hover) #sort[data-preview] {
display: flex;
mo-icon-button {
color: var(--mo-color-gray);
opacity: 0.5;
}
}
#menu-icon {
position: absolute;
inset-inline-end: calc(var(--mo-data-grid-cell-padding) - 6px);
inset-block-start: 2px;
opacity: 0;
font-size: 20px;
transition: 0.1s;
}
:host(:hover), :host([menuOpen]) {
#container {
margin-inline-end: 20px;
}
#menu-icon {
opacity: 1;
}
}
mo-menu {
position-anchor: --mo-data-grid-column-header;
&::part(popover) {
margin-inline-start: 4px;
}
mo-heading {
padding: 0.5rem 1rem;
opacity: 0.5;
}
}
`;
}
get template() {
if (this.column.sticky) {
this.style.insetInline = this.column.stickyColumnInsetInline;
}
if (!this.column.sticky) {
this.removeAttribute('data-sticky');
}
else {
this.setAttribute('data-sticky', this.column.sticky);
}
const stickyEdge = this.column.stickyEdge;
stickyEdge ? this.setAttribute('data-sticky-edge', stickyEdge) : this.removeAttribute('data-sticky-edge');
const direction = this.column.alignment === 'end' ? 'horizontal-reversed' : 'horizontal';
return html `
<mo-flex id='container' alignItems='center' gap='0.2rem'
direction=${direction}
=${() => this.column.toggleSort()}
=${(e) => { e.preventDefault(); this.menuOpen = true; }}
${style({ flex: '1', overflow: 'hidden' })}
>
${this.contentTemplate}
${this.sortingTemplate}
</mo-flex>
${this.menuTemplate}
${this.separatorTemplate}
`;
}
get contentTemplate() {
return html `
<div id='content'
${style({ textAlign: this.column.alignment })}
${!this.column.description ? html.nothing : tooltip(this.column.description)}
>${this.column.heading}</div>
`;
}
get sortingTemplate() {
const sortingDefinition = this.column.sortingDefinition;
const sortIcon = sortingDefinition?.strategy === DataGridSortingStrategy.Ascending ? 'arrow_upward' : 'arrow_downward';
const sortingRank = !sortingDefinition || this.column.dataGrid.getSorting().length <= 1 ? undefined : sortingDefinition.rank;
return !this.column.sortable ? html.nothing : html `
<mo-flex id='sort' direction='horizontal' ?data-preview=${!sortingDefinition?.strategy}>
<mo-icon-button dense icon=${sortIcon}></mo-icon-button>
${!sortingRank ? html.nothing : html `<span>${sortingRank}</span>`}
</mo-flex>
`;
}
get menuTemplate() {
const additionalItems = this.column.getMenuItemsTemplate?.();
return html `
<mo-popover-container placement='block-end' alignment='end' style='display: contents'>
<mo-icon-button dense id='menu-icon' icon='more_vert'></mo-icon-button>
<mo-menu slot='popover' .anchor=${this} target='menu-icon' ?open=${bind(this, 'menuOpen')}>
<mo-line></mo-line>
${join([
!this.column.sortable ? undefined : this.getSortingItemsTemplate(additionalItems instanceof Map ? additionalItems.get('sorting') : undefined),
// Hide stickiness items for now
true ? undefined : this.getStickinessItemsTemplate(additionalItems instanceof Map ? additionalItems.get('stickiness') : undefined),
this.getMoreItemsTemplate(additionalItems instanceof Map ? additionalItems.get('more') : additionalItems),
].filter(Boolean), html `<mo-line></mo-line>`)}
</mo-menu>
</mo-popover-container>
`;
}
getSortingItemsTemplate(additionalItems) {
return html `
<mo-heading typography='subtitle2'>${t('Sorting')}</mo-heading>
<mo-selectable-menu-item icon='arrow_downward'
?disabled=${this.column.sortable === false}
?selected=${this.column.sortingDefinition?.strategy === DataGridSortingStrategy.Descending}
=${() => this.column.toggleSort(DataGridSortingStrategy.Descending)}
>${t('Sort descending')}</mo-selectable-menu-item>
<mo-selectable-menu-item icon='arrow_upward'
?disabled=${this.column.sortable === false}
?selected=${this.column.sortingDefinition?.strategy === DataGridSortingStrategy.Ascending}
=${() => this.column.toggleSort(DataGridSortingStrategy.Ascending)}
>${t('Sort ascending')}</mo-selectable-menu-item>
${additionalItems}
`;
}
getStickinessItemsTemplate(additionalItems) {
return html `
<mo-heading typography='subtitle2'>${t('Stickiness')}</mo-heading>
<mo-selectable-menu-item icon='push_pin'
?selected=${this.column.sticky === 'start'}
=${() => this.column.toggleSticky('start')}
>${t('Stick to start')}</mo-selectable-menu-item>
<mo-selectable-menu-item icon='push_pin'
?selected=${this.column.sticky === 'both'}
=${() => this.column.toggleSticky('both')}
>${t('Stick to both')}</mo-selectable-menu-item>
<mo-selectable-menu-item icon='push_pin'
?selected=${this.column.sticky === 'end'}
=${() => this.column.toggleSticky('end')}
>${t('Stick to end')}</mo-selectable-menu-item>
${additionalItems}
`;
}
getMoreItemsTemplate(additionalItems) {
return html `
<mo-menu-item icon='visibility_off' =${() => this.column.hide()}>
${t('Hide')}
</mo-menu-item>
${additionalItems}
`;
}
get separatorTemplate() {
if (!this.column.dataGrid) {
return html.nothing;
}
const index = this.column.dataGrid.visibleColumns.indexOf(this.column);
return html `
<mo-data-grid-header-separator
?data-last=${this.column.dataGrid.visibleColumns.length - 1 === index}
.dataGrid=${this.column.dataGrid}
.column=${this.column.dataGrid.visibleColumns[index]}
></mo-data-grid-header-separator>
`;
}
};
__decorate([
property({ type: Object })
], DataGridColumnHeader.prototype, "column", void 0);
__decorate([
property({ type: Boolean, reflect: true })
], DataGridColumnHeader.prototype, "menuOpen", void 0);
DataGridColumnHeader = __decorate([
component('mo-data-grid-column-header')
], DataGridColumnHeader);
export { DataGridColumnHeader };