@telekom/scale-components
Version:
Scale is the digital design system for Telekom products and experiences.
1,118 lines (1,103 loc) • 68.1 kB
JavaScript
import { h, proxyCustomElement, HTMLElement, createEvent, Host } from '@stencil/core/internal/client';
import { c as classnames } from './index2.js';
import { e as emitEvent } from './utils.js';
import { d as defineCustomElement$x } from './button.js';
import { d as defineCustomElement$w } from './checkbox.js';
import { d as defineCustomElement$v } from './dropdown-select.js';
import { d as defineCustomElement$u } from './dropdown-select-item.js';
import { d as defineCustomElement$t } from './helper-text.js';
import { d as defineCustomElement$s } from './icon.js';
import { d as defineCustomElement$r } from './action-checkmark.js';
import { d as defineCustomElement$q } from './action-close.js';
import { d as defineCustomElement$p } from './action-hide-password.js';
import { d as defineCustomElement$o } from './action-minus.js';
import { d as defineCustomElement$n } from './action-sort.js';
import { d as defineCustomElement$m } from './action-success.js';
import { d as defineCustomElement$l } from './alert-error.js';
import { d as defineCustomElement$k } from './alert-information.js';
import { d as defineCustomElement$j } from './content-sort-indicator-down.js';
import { d as defineCustomElement$i } from './content-sort-indicator-up.js';
import { d as defineCustomElement$h } from './navigation-collapse-down.js';
import { d as defineCustomElement$g } from './navigation-collapse-up.js';
import { d as defineCustomElement$f } from './navigation-double-left.js';
import { d as defineCustomElement$e } from './navigation-double-right.js';
import { d as defineCustomElement$d } from './navigation-left.js';
import { d as defineCustomElement$c } from './navigation-right.js';
import { d as defineCustomElement$b } from './service-settings.js';
import { d as defineCustomElement$a } from './link.js';
import { d as defineCustomElement$9 } from './menu-flyout.js';
import { d as defineCustomElement$8 } from './menu-flyout-item.js';
import { d as defineCustomElement$7 } from './menu-flyout-list.js';
import { d as defineCustomElement$6 } from './pagination.js';
import { d as defineCustomElement$5 } from './progress-bar.js';
import { d as defineCustomElement$4 } from './switch.js';
import { d as defineCustomElement$3 } from './tag.js';
import { d as defineCustomElement$2 } from './text-field.js';
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: boolean, eg `true`
// Options
// style?: string 'switch' | 'checkbox'
// editable?: boolean = false
const CheckboxCell = {
defaults: {
sortBy: 'number',
},
getLongestContent({ rows, columnIndex }) {
// Skip check as content width is always the same
return rows[0][columnIndex];
},
render: ({ field, content, component, rowIndex, columnIndex }) => {
const { style = 'checkbox', editable = false, label } = field;
const props = {
checked: content,
disabled: !editable,
label,
};
if (editable) {
props.onScaleChange = (ev) => {
const { value } = ev.detail;
// Update rows data
component.rows[rowIndex][columnIndex] = value;
// Trigger event
component.triggerEditEvent(value, rowIndex, columnIndex);
};
}
switch (style) {
case 'switch':
return h("scale-switch", Object.assign({ size: "small" }, props));
default:
// 'checkbox'
return h("scale-checkbox", Object.assign({}, props));
}
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected: date/time string, eg '10:23:00'
// TODO: see if this is even worth it. It may help with sorting/filtering?
// work out format requirements - as date/time formatting is heavy eg moment.js
// const { inputFormat, outputFormat } = field;
// inputFormat: 'HH:mm:ss', // ['timestamp', '']
// outputFormat: 'HH:mm',
const DateCell = {
defaults: {
sortBy: 'text',
},
render: ({ content, isAutoWidthCheck }) => {
let value = content;
// Render all digits with 8s as they're the widest
if (isAutoWidthCheck) {
value = value.replace(/[0-9]/g, '8');
}
return h("p", { class: `scl-body` }, value);
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: an email string (eg: 'mailto:example@domain.com)
const EmailCell = {
defaults: {
sortBy: 'text',
},
render: ({ content }) => {
// Remove protocol (mailto:)
const emailNoProtocol = content.replace(/^mailto:/i, '');
return h("scale-link", { href: content }, emailNoProtocol);
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: number, eg 10230.32
// Options
// style?: string 'bar' | 'progress'
// min?: number 0
// max?: number 100
const GraphCell = {
defaults: {
sortBy: 'number',
},
render: ({ field, content }) => {
const { style = 'progress', min = 0, max = 100 } = field;
// Convert content to 0>100 range for progress bar
const progress = parseFloat((((content - min) / (max - min)) * 100).toPrecision(String(content).replace('.', '').length));
// I really don't know the difference between bar and progress
switch (style) {
case 'bar':
return (h("div", { class: `tbody__bar-cell` },
h("scale-progress-bar", { class: "data-grid-progress-bar", "aria-hidden": "true", percentage: progress,
// showStatus={true}
mute: true }),
h("p", { class: `scl-body` }, content)));
default:
// progress
return (h("scale-progress-bar", { class: "data-grid-progress-bar", percentage: progress, showStatus: true, mute: true }));
}
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: a url string (eg: 'https://sample.com')
const LinkCell = {
defaults: {
sortBy: 'text',
},
render: ({ content }) => {
// Remove protocol (http/https)
const urlNoProtocol = content.replace(/^https?\:\/\//i, '');
return (h("scale-link", { href: content, target: "_blank" }, urlNoProtocol));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: HTMLElement
const HTMLCell = {
defaults: {},
getLongestContent({ rows, columnIndex }) {
// Skip check as content width is always the same
return rows[0][columnIndex];
},
render: ({ content, component }) => {
return (content && (h("scale-button", { variant: "secondary", size: "small", "icon-only": true, "inner-aria-label": `Activate to ${content.isExpanded ? 'collapse' : 'expand'} content`, onClick: () => {
content.isExpanded = !content.isExpanded;
component.forceRender++;
} }, content.isExpanded ? (h("scale-icon-navigation-collapse-up", { size: 14 })) : (h("scale-icon-navigation-collapse-down", { size: 14 })))));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: number or string, eg `120.0`
// Options
// precision
// decimalSymbol
// groupSymbol
// editable?: boolean = false / removed/deprecated in 3.0.0-beta.135
const NumberCell = {
defaults: {
textAlign: 'right',
sortBy: 'number',
},
render: ({ field, content,
// component,
// rowIndex,
// columnIndex,
isAutoWidthCheck, }) => {
const { precision = Infinity, decimalSymbol = '.', groupSymbol = '', prefix = '', suffix = '', } = field;
// Input component doesn't expand with content, so need to return a fake element that simulates width
// if (isAutoWidthCheck && editable) {
// return (
// <p class={`scl-body`} style={{ paddingRight: '26px' }}>
// {content}
// </p>
// );
// }
// const step = `0.${(String(content).split('.')[1] || '')
// .split('')
// .map(() => '0')}`.replace(/,/g, '');
// if (editable) {
// const props = {
// type: 'number',
// size: 'small',
// step: step.slice(0, step.length - 1) + '1',
// value: String(content),
// styles: /* css */ `.text-field__control {
// text-align: right !important;
// }`,
// label,
// } as any;
// // TODO: use blur to reduce number of changes - but doesn't pass value
// props.onScaleChange = ({ detail }) => {
// const { value } = detail;
// // Update rows data
// component.rows[rowIndex][columnIndex] = value;
// // Trigger event
// component.triggerEditEvent(value, rowIndex, columnIndex);
// };
// return <scale-text-field {...props}></scale-text-field>;
// } else {
// }
let value = content;
// Render all digits with 8s as they're the widest
if (isAutoWidthCheck) {
value = Number(value.toString().replace(/[0-9]/g, '8'));
}
// Refine to requested decimal precision
if (precision < 100) {
value = Number(value).toFixed(precision);
}
else {
value = value.toString();
}
// Replace/add requested delimiters
if (groupSymbol || decimalSymbol !== '.') {
const parts = value.split('.');
if (groupSymbol) {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, groupSymbol);
}
value = parts.join(decimalSymbol);
}
// Add prefix/suffix
if (prefix || suffix) {
value = prefix + value + suffix;
}
return (h("p", { class: `scl-body`, style: { textAlign: 'right' } }, value));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected: string
// Options
// options: string array
// editable?: boolean = false
const SelectCell = {
defaults: {
sortBy: 'text',
},
render: ({ field, content, component, rowIndex, columnIndex, isAutoWidthCheck, }) => {
const { options, editable = false, label } = field;
// Select component doesn't expand with content, so need to return a fake element that simulates width
if (isAutoWidthCheck) {
return (h("p", { class: `scl-body`, style: { paddingRight: '56px' } }, content));
}
const props = {
disabled: !editable,
value: content,
label,
};
if (editable) {
props.onScaleChange = ({ detail }) => {
const { value } = detail;
// Update rows data
component.rows[rowIndex][columnIndex] = value;
// Trigger event
component.triggerEditEvent(value, rowIndex, columnIndex);
};
}
return (h("scale-dropdown-select", Object.assign({ floatingStrategy: "fixed" }, props), options.map((option) => {
return (h("scale-dropdown-select-item", { value: option }, option));
})));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected: comma delimited string (eg 'one, two, three')
const TagsCell = {
defaults: {
sortBy: 'text',
},
render: ({ content }) => {
const tags = content.split(',').map((s) => s.trim());
return (h("ul", { class: `tbody__tag-list` }, tags.map((tag) => (h("li", null,
h("scale-tag", { size: "small", type: "strong" }, tag))))));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: a telephone number string (eg: 'tel:+491234567')
const TelephoneCell = {
defaults: {
sortBy: 'text',
},
render: ({ content }) => {
// Remove protocol (tell:)
const telephoneNoProtocol = content.replace(/^tel:/i, '');
return h("scale-link", { href: content }, telephoneNoProtocol);
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// Expected content: unformated string 'this is a string'
// Options
// variant?: string 'body' | 'h6' | 'h5' | etc
// editable?: boolean = false
// iconPrefix?: string eg 'action-download'
// iconSuffix?: string eg 'action-download'
const TextCell = {
defaults: {
sortBy: 'text',
},
render: ({ field, content, component, rowIndex, columnIndex, isAutoWidthCheck, }) => {
const { variant = 'body', editable = false, iconPrefix, iconSuffix, label, } = field;
// Input component doesn't expand with content, so need to return a fake element that simulates width
if (isAutoWidthCheck && editable) {
return (h("p", { class: `scl-body`, style: { paddingRight: '26px' } }, content));
}
if (editable) {
const props = {
type: 'text',
value: content,
label,
};
// TODO: use blur to reduce number of changes - but doesn't pass value
// TODO: apply variant and iconPrefix/Suffix to editable text
props.onScaleChange = ({ detail }) => {
const { value } = detail;
// Update rows data
component.rows[rowIndex][columnIndex] = value;
// Trigger event
component.triggerEditEvent(value, rowIndex, columnIndex);
};
return h("scale-text-field", Object.assign({}, props));
}
else {
let value = content;
// Add an extra couple of characters for the width check to avoid clipping
if (isAutoWidthCheck) {
value += 'w';
}
return (h("div", { class: `tbody__text-cell` },
iconPrefix && (h("span", { class: `tbody__text-cell-prefix` }, h(`scale-icon-${iconPrefix}`))),
h("p", { class: `scl-${variant}` }, value),
iconSuffix && (h("span", { class: `tbody__text-cell-suffix` }, h(`scale-icon-${iconSuffix}`)))));
}
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
var __rest = (undefined && undefined.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
const ActionsCell = {
defaults: {},
render: ({ content }) => {
return (h("div", { class: `tbody__actions` }, content.map((action) => {
const { label } = action, props = __rest(action, ["label"]);
if (typeof label === 'object' && '__html' in label) {
return (h("scale-button", Object.assign({ size: "small", innerHTML: label.__html }, props)));
}
return (h("scale-button", Object.assign({ size: "small" }, props), label));
})));
},
};
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
const CELL_TYPES = {
checkbox: CheckboxCell,
date: DateCell,
email: EmailCell,
graph: GraphCell,
html: HTMLCell,
link: LinkCell,
number: NumberCell,
select: SelectCell,
tags: TagsCell,
telephone: TelephoneCell,
text: TextCell,
actions: ActionsCell,
};
// Fallback if no type set on field
const DEFAULT_CELL_TYPE = 'text';
// Common cell defaults, can be overridden in cell type classes
const CELL_DEFAULTS = {
maxWidth: Infinity,
minWidth: 20,
resizable: true,
sortable: false,
sortBy: 'text',
textAlign: 'left',
visible: true,
width: 'auto',
};
const dataGridCss = ".scl-body{margin:0;font:var(--telekom-text-style-body)}.scl-label{margin:0;font:var(--telekom-text-style-small)}.scl-h1{margin:0;font:var(--telekom-text-style-heading-1)}.scl-h2{margin:0;font:var(--telekom-text-style-heading-2)}.scl-h3{margin:0;font:var(--telekom-text-style-heading-3)}.scl-h4{margin:0;font:var(--telekom-text-style-heading-4)}.scl-h5{margin:0;font:var(--telekom-text-style-heading-5)}.scl-h6{margin:0;font:var(--telekom-text-style-heading-6)}:host{font-family:var(--telekom-typography-font-family-sans);font-size:var(--telekom-typography-font-size-body);font-weight:var(--telekom-typography-font-weight-regular);line-height:var(--telekom-typography-line-spacing-standard);color:var(--telekom-color-text-and-icon-standard)}.data-grid input,.data-grid select{letter-spacing:inherit;font-weight:inherit;font-family:inherit;line-height:inherit}.data-grid{position:relative;display:block;background:var(--telekom-color-ui-state-fill-standard);border-radius:var(--telekom-radius-large);border:1px solid var(--telekom-color-ui-faint);overflow:hidden}.data-grid--hide-border{border:none}.data-grid__auto-width-check{opacity:0}.data-grid__title-block{display:flex;align-items:center;justify-content:flex-end;padding-right:var(--telekom-spacing-composition-space-06);padding-left:var(--telekom-spacing-composition-space-08)}.data-grid__heading{flex-grow:1}.data-grid__title-block ::slotted(*){margin-left:var(--telekom-spacing-composition-space-04)}.data-grid__settings-menu{margin-left:var(--telekom-spacing-composition-space-04)}.data-grid__scroll-container{overflow:auto;overflow-x:overlay;overflow-y:overlay;ms-overflow-style:-ms-autohiding-scrollbar;scrollbar-gutter:stable}.data-grid__table{border-spacing:0;border-collapse:collapse;overflow:hidden}.data-grid--hide-menu .data-grid__settings-menu{display:none}.data-grid:not(.data-grid--hide-menu) .data-grid__title-block{min-height:72px}.data-grid--hide-menu .data-grid__title-block{padding-right:var(--telekom-spacing-composition-space-06)}.thead{display:block;white-space:nowrap;border-bottom:1px solid var(--telekom-color-ui-faint);position:relative;background:var(--telekom-color-ui-state-fill-standard);z-index:1}.data-grid--freeze-header .thead{z-index:30;background-color:var(--telekom-color-background-canvas)}.thead-sortable{cursor:pointer}.thead-sortable:focus{box-shadow:inset 0 0 0 var(--telekom-spacing-composition-space-02)\n var(--telekom-color-functional-focus-standard)}.thead__cell{display:inline-flex;align-items:center;height:var(--telekom-spacing-composition-space-10);text-align:left;user-select:none;position:relative;padding:0 var(--telekom-spacing-composition-space-06);color:var(--telekom-color-text-and-icon-additional)}.thead__cell--numbered{text-align:right;justify-content:flex-end}.thead__cell--selection{justify-content:center;text-align:center}.thead__cell--selection xds-checkbox::part(container){justify-content:center}.thead__title{color:var(--telekom-color-text-and-icon-standard)}.thead__text{font-size:var(--telekom-typography-font-size-small);line-height:var(--telekom-typography-line-spacing-standard);position:relative}.thead__arrow-top,.thead__arrow-bottom{position:absolute;display:none !important;top:-2px;left:-16px}.thead__sort-prompt{position:absolute;top:0;left:0;width:100%;height:100%;margin:0;background:none;border:0;opacity:1;cursor:pointer}.thead__divider{position:absolute;right:calc(-1 * var(--telekom-spacing-composition-space-04));bottom:0px;height:100%;padding:19px var(--telekom-spacing-composition-space-04) 0px;box-sizing:border-box;cursor:col-resize;z-index:1}.thead__divider-line{pointer-events:none;height:100%;width:1px;background:var(--telekom-color-ui-faint)}.thead__cell:first-child{padding-left:var(--telekom-spacing-composition-space-08)}.thead__cell:focus{outline:none}.thead__cell[aria-sort='ascending'] .thead__arrow-top{display:inline-flex !important}.thead__cell[aria-sort='descending'] .thead__arrow-bottom{display:inline-flex !important}.thead__cell[aria-sort]:hover{color:var(--telekom-color-text-and-icon-primary-hovered)}.thead__cell[aria-sort='none']:hover .thead__arrow-top,.thead__cell[aria-sort='none']:hover .thead__arrow-bottom{display:none !important}.thead__cell[aria-sort='ascending']:hover .thead__arrow-top{color:var(--telekom-color-text-and-icon-primary-hovered)}.thead__cell[aria-sort='descending']:hover .thead__arrow-bottom{color:var(--telekom-color-text-and-icon-primary-hovered)}.tbody{display:block}.tbody__row{display:block;white-space:nowrap}.tbody__mobile-title{display:none}.tbody__mobile-label{display:none}.tbody__cell{display:inline-block;margin:8px;padding:8px;overflow:hidden;}.tbody__cell--numbered{text-align:right}.tbody__cell--selection{justify-content:center;text-align:center}.tbody__cell--selection scale-checkbox::part(container),.tbody__cell--selection scale-checkbox [part='container']{justify-content:center}.tbody__cell scale-checkbox{width:auto}.tbody__nested{white-space:nowrap;padding:0px;margin:0px}.tbody__nested-cell{display:block;padding:var(--telekom-spacing-composition-space-06);margin:0px}.tbody__cell:first-of-type{margin-left:var(--telekom-spacing-composition-space-06);}.tbody__nested-cell:first-child{margin-left:0px}.data-grid--shade-alternate .tbody__row:nth-of-type(even),.data-grid--shade-alternate .tbody__nested:nth-of-type(even){background:var(--telekom-color-background-surface-subtle)}.data-grid__auto-width-check .tbody__cell{padding:0}.tbody__tag-list{list-style:none;padding:0;margin:0}.tbody__tag-list li{display:inline-block;margin-right:8px}.tbody__tag-list li:last-child{margin-right:0}.data-grid input[type='checkbox']{display:block;height:14px;margin:5px 4px}.tbody__text-cell{display:flex;align-items:center}.tbody__text-cell-prefix{display:inline-flex;align-items:center;margin-right:0.5em}.tbody__text-cell-suffix{display:inline-flex;align-items:center;margin-left:0.5em}.tbody__cell p{overflow:hidden;text-overflow:ellipsis}.tbody__cell scale-link{overflow:hidden;text-overflow:ellipsis}.tbody__bar-cell{display:inline-flex;width:100%}.tbody__cell scale-progress-bar{flex-grow:1}.tbody__actions scale-button{margin-right:var(--telekom-spacing-composition-space-04)}.data-grid-progress-bar::part(progress-bar){min-width:50px;max-width:200px}.data-grid-progress-bar::part(status){padding-top:0}.info{height:44px;position:relative;border-top:var(--telekom-line-weight-standard) solid\n var(--telekom-color-ui-subtle);display:flex;justify-content:center}.info__selection{position:absolute;bottom:0;line-height:54px;left:var(--telekom-spacing-composition-space-08)}.data-grid--hide-border:not(.data-grid--mobile) .info__pagination{border-bottom:1px solid var(--telekom-color-ui-subtle);border-right:1px solid var(--telekom-color-ui-subtle)}.data-grid--mobile{border:none;background:none}.data-grid--mobile .data-grid__title-block{padding-left:0;padding-right:0}.data-grid--hide-menu.data-grid--mobile .data-grid__title-block{padding-right:0}.data-grid--mobile .data-grid__settings-menu{right:0}.data-grid--mobile .data-grid__scroll-container{height:auto !important}.data-grid--mobile .data-grid__table{display:block;height:auto !important}.data-grid--mobile .thead{display:none}.data-grid--mobile .tbody{display:block}.data-grid--mobile .tbody__row{display:block;position:relative;white-space:initial;margin:0 0 var(--telekom-spacing-composition-space-04);padding:var(--telekom-spacing-composition-space-08);border-radius:var(--telekom-radius-standard);background:var(--telekom-color-background-surface);border:1px solid var(--telekom-color-ui-faint)}.data-grid--mobile .tbody__row:hover{background:var(--telekom-color-background-surface)}.data-grid--mobile .tbody__mobile-title{display:block;margin-bottom:var(--telekom-spacing-composition-space-04)}.data-grid--mobile .tbody__mobile-label{display:block}.data-grid--mobile .tbody__cell{display:flex;align-items:center;width:auto !important;padding:5px 0;margin:0;min-height:var(--telekom-spacing-composition-space-08);line-height:var(--telekom-spacing-composition-space-08);overflow:auto;overflow-x:hidden}.data-grid--mobile .tbody__cell--used-as-mobile-title{display:none}.data-grid--mobile .tbody__mobile-label{display:block;width:100px;flex-shrink:0;color:var(--telekom-color-text-and-icon-additional);font-size:var(--telekom-typography-font-size-small);font-weight:var(--telekom-typography-font-weight-medium)}.data-grid--mobile .tbody__cell:first-child{margin-left:0px}.data-grid--mobile .tbody__cell--selection{position:absolute;top:19px;right:12px}.data-grid--mobile .tbody__cell--numbered{position:absolute;top:19px;right:56px}.data-grid--mobile .tbody__cell scale-text-field,.data-grid--mobile .tbody__cell scale-dropdown{width:100%}.data-grid--mobile .tbody__nested{width:auto !important}.data-grid--mobile .tbody__nested-cell{padding:0;margin-bottom:var(--telekom-spacing-composition-space-04)}.data-grid--mobile.data-grid--shade-alternate .tbody__row:nth-of-type(even){background:var(--telekom-color-background-surface)}.data-grid--mobile .info{height:auto;border-top:none;text-align:center}.data-grid--mobile .info__selection{position:relative;left:0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}";
/* Reused Private Variables */
let resizeObserver;
const name = 'data-grid';
const DataGrid = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
/* 6. Lifecycle Events (call order) */
constructor() {
super();
this.__registerHost();
this.__attachShadow();
this.scaleEdit = createEvent(this, "scale-edit", 7);
this.scaleEditLegacy = createEvent(this, "scaleEdit", 7);
this.scaleSort = createEvent(this, "scale-sort", 7);
this.scaleSortLegacy = createEvent(this, "scaleSort", 7);
/* 2. State Variables (alphabetical) */
/** Used to force render after sorting/selection */
this.forceRender = 0;
/** Pagination starting index */
this.paginationStart = 0;
/** Table scroll value for frozen header */
this.scrollY = 0;
/** (optional) Freeze header row from scrolling */
this.freezeHeader = false;
/** (optional) Heading string */
this.heading = '';
/** (optional) Set to true to remove border */
this.hideBorder = false;
/** (optional) Set to true to hide header row */
this.hideHeader = false;
/** (optional) Set to true to remove info footer block including pagination and selection status */
this.hideInfo = false;
/** (optional) Set to true to hide settings menu */
this.hideMenu = false;
/** (optional) Set to true to add numbers column */
this.numbered = false;
/** (optional) Set number of rows to display per pagination page */
this.pageSize = Infinity;
/** (optional) Set to true to add selection column */
this.selectable = false;
/** Read-only selection array - populated with raw data from selected rows */
this.selection = [];
/** (optional) Shade every second row darker */
this.shadeAlternate = true;
/** (optional) Set to false to hide table, used for nested tables to re-render upon toggle */
this.visible = true;
/** Stored active sorting column index, for state removal */
this.activeSortingIndex = -1;
/** Track component width to constrict nested content, which is necessary with table layout */
this.contentWidth = 100;
/** Flag to know to check for data completeness */
this.dataNeedsCheck = true;
/** Flag to know if rendering can commence */
this.hasData = false;
/** Flag that is true when width below a certain limit */
this.isMobile = false;
/** Flag that enough data supplied to warrant pagination */
this.isPagination = false;
/** Flag that is true if any fields are sortable */
this.isSortable = false;
/** Track container width to avoid re-calculating column stretching */
this.lastContainerWidth = 100;
/** Index of field to use as mobile title, if any */
this.mobileTitleIndex = -1;
/** Determine if auto-width parsing needed */
this.needsAutoWidthParse = false;
/** Force column resize after render */
this.needsColumnResize = false;
/** Auto-calculated number column width */
this.numberColumnWidth = 0;
/** Selection column width */
this.selectionColumnWidth = 22;
this.handleMenuListClick = (event) => {
const menuItems = ['sortBy', 'toggleVisibility'];
const currentMenuItemsIndex = menuItems.indexOf(event.target.id);
if (currentMenuItemsIndex > -1) {
// check if there is already opened flyout menu list with different id, if opened, close it
const inactiveMenuItem = this.hostElement.shadowRoot.querySelector(`#${menuItems[1 - currentMenuItemsIndex]}List`);
if (inactiveMenuItem) {
inactiveMenuItem.setAttribute('opened', 'false');
}
}
};
// Bind certain callbacks to scope
this.onDividerMove = this.onDividerMove.bind(this);
this.onDividerUp = this.onDividerUp.bind(this);
this.applyResponsiveClasses = this.applyResponsiveClasses.bind(this);
this.updateColumnStretching = this.updateColumnStretching.bind(this);
}
componentWillLoad() {
this.fieldsHandler();
this.rowsHandler();
}
componentWillUpdate() { }
componentDidRender() {
if (this.needsAutoWidthParse) {
this.calculateAutoWidths();
}
// Wait a frame to avoid warning about possible infinite loop
setTimeout(() => {
if (this.needsColumnResize) {
this.updateColumnStretching();
}
});
}
componentDidLoad() {
this.addResizeObserver();
}
componentDidUpdate() { }
disconnectedCallback() {
this.removeResizeObserver();
}
/* 7. Listeners */
fieldsHandler() {
this.parseFields();
this.checkForMobileTitle();
this.checkForSortableFields();
this.dataNeedsCheck = true;
}
rowsHandler() {
// Reset pagination to the last page of the new records if new records are less than previous.
if (this.paginationStart > this.rows.length) {
this.paginationStart =
this.rows.length - (this.rows.length % this.pageSize);
}
this.parseRows();
this.setInitialRowProps();
this.resetSortingToggle();
this.dataNeedsCheck = true;
// Set flag to dirty to redo column width with new data
this.needsAutoWidthParse = true;
this.needsColumnResize = true;
if (
// when we run out of items on the current page
this.rows.length <= this.paginationStart &&
// and we are NOT on the first page
this.paginationStart - this.pageSize > -1) {
// step back one page
this.paginationStart = this.paginationStart - this.pageSize;
}
}
/* 8. Public Methods */
/* 9. Local Methods */
parseFields() {
if (this.fields && typeof this.fields === 'string') {
this.fields = JSON.parse(this.fields);
}
}
parseRows() {
if (this.rows && typeof this.rows === 'string') {
this.rows = JSON.parse(this.rows);
}
}
setInitialRowProps() {
if (!this.rows || !this.rows.length) {
return;
}
this.rows.forEach((row, i) => {
// Store indices of original order on rows for resetting sorting
row.initialIndex = i;
// Set initial selected flag
row.selected = false;
});
// Determine if pagination will be required
this.isPagination = this.pageSize <= this.rows.length - 1;
}
checkHasData() {
// Need both fields and data content in order to populate
if (!this.fields) {
return false;
}
for (let i = 0; i < this.fields.length; i++) {
// Use default type if none set
if (!this.fields[i].type) {
this.fields[i].type = DEFAULT_CELL_TYPE;
}
if (!CELL_TYPES[this.fields[i].type]) {
// tslint:disable-next-line: no-console
console.warn(`Unrecognised field type: "${this.fields[i].type}"`);
return false;
}
}
if (!this.rows || !this.rows.length) {
return false;
}
for (let i = 0; i < this.rows.length; i++) {
if (this.rows[i].length !== this.fields.length) {
// tslint:disable-next-line: no-console
console.warn(`Unable to render ${this.heading && `"${this.heading}" `}table: row data length not equal to supplied fields.`);
return false;
}
}
return true;
}
checkForMobileTitle() {
// Reset for new data
this.mobileTitleIndex = -1;
if (!this.fields) {
return;
}
this.fields.every(({ mobileTitle }, i) => {
if (mobileTitle) {
this.mobileTitleIndex = i;
return false;
}
return true;
});
}
checkForSortableFields() {
this.isSortable = false;
if (!this.fields) {
return;
}
this.fields.forEach(({ sortable }) => {
if (sortable) {
this.isSortable = true;
}
});
}
getCssClassMap() {
return classnames(name, !this.isMobile && `${name}--desktop`, this.isMobile && `${name}--mobile`, this.shadeAlternate && `${name}--shade-alternate`, this.freezeHeader && `${name}--freeze-header`, this.hideBorder && `${name}--hide-border`, this.hideMenu && `${name}--hide-menu`);
}
polyfillMousePosition(e) {
// For touch
if (e.changedTouches && e.changedTouches.length) {
e.x = e.changedTouches[0].pageX;
e.y = e.changedTouches[0].pageY;
}
// For cross browser support
if (e.x === undefined) {
e.x = e.clientX;
e.y = e.clientY;
}
}
getDefaultLongestContent({ rows, columnIndex }) {
let maxLength = 0;
let longestContent;
rows.forEach((row) => {
const length = row[columnIndex].toString().length;
if (length > maxLength) {
longestContent = row[columnIndex];
maxLength = length;
}
});
return longestContent;
}
// Selection handlers
toggleSelectAll() {
if (!this.elToggleSelectAll) {
return;
}
this.rows.forEach((row) => (row.selected = this.elToggleSelectAll.checked));
this.updateReadableSelection();
this.forceRender++;
}
toggleRowSelect({ target }, rowIndex) {
this.rows[rowIndex].selected = target.checked;
this.updateReadableSelection();
this.forceRender++;
}
updateReadableSelection() {
this.selection.length = 0;
this.rows.forEach((row) => row.selected && this.selection.push(row));
// Check header checkbox if any or none are selected
const selectAll = this.hostElement.shadowRoot.querySelector('.thead__cell--selection scale-checkbox');
selectAll.checked = !!this.selection.length;
// selectAll.indeterminate = !!this.selection.length;
}
// Sorting handlers
toggleTableSorting(sortDirection, columnIndex, type) {
// Remove sorting from previous column index
if (this.activeSortingIndex > -1 &&
this.activeSortingIndex !== columnIndex) {
this.fields[this.activeSortingIndex].sortDirection = 'none';
}
// Store new column index
this.activeSortingIndex = columnIndex;
const newSortDirection = sortDirection === 'none'
? 'ascending'
: sortDirection === 'ascending'
? 'descending'
: 'none';
this.fields[columnIndex].sortDirection = newSortDirection;
this.sortTable(newSortDirection, type, columnIndex);
}
sortTable(sortDirection, type, columnIndex) {
if (sortDirection === 'none') {
this.rows.sort((a, b) => {
return a.initialIndex - b.initialIndex;
});
}
else {
switch ((CELL_TYPES[type] &&
CELL_TYPES[type].defaults &&
CELL_TYPES[type].defaults.sortBy) ||
CELL_DEFAULTS.sortBy) {
case 'text':
if (sortDirection === 'ascending') {
this.rows.sort((a, b) => {
const textA = a[columnIndex].toLowerCase();
const textB = b[columnIndex].toLowerCase();
return textA < textB ? -1 : textA > textB ? 1 : 0;
});
}
else {
this.rows.sort((a, b) => {
const textA = a[columnIndex].toLowerCase();
const textB = b[columnIndex].toLowerCase();
return textA > textB ? -1 : textA < textB ? 1 : 0;
});
}
break;
case 'number':
if (sortDirection === 'ascending') {
this.rows.sort((a, b) => {
return Number(a[columnIndex]) - Number(b[columnIndex]);
});
}
else {
this.rows.sort((a, b) => {
return Number(b[columnIndex]) - Number(a[columnIndex]);
});
}
break;
}
}
this.forceRender++;
// Trigger event
this.triggerSortEvent(sortDirection, type, columnIndex);
}
resetSortingToggle() {
if (this.activeSortingIndex > -1) {
this.fields[this.activeSortingIndex].sortDirection = 'none';
}
this.activeSortingIndex = -1;
}
// Column resize handlers
onDividerDown(e) {
this.polyfillMousePosition(e);
// For touch - Prevent mousedown firing, and native scroll
e.preventDefault();
// Store divider elem for use in move and end events
this.activeDivider = e.target;
// Store initial value to calculate change
e.target.downX = e.x;
// Reset to avoid reapplying previous change
this.activeDivider.interactiveWidth = 0;
window.addEventListener('mousemove', this.onDividerMove);
window.addEventListener('touchmove', this.onDividerMove);
window.addEventListener('mouseup', this.onDividerUp);
window.addEventListener('touchend', this.onDividerUp);
}
onDividerMove(e) {
// TODO: calculate width stretchWidth to drop in correct location
this.polyfillMousePosition(e);
const { width, min, max } = this.activeDivider.dataset;
const diff = e.x - this.activeDivider.downX;
const newWidth = Math.min(Number(max), Math.max(Number(min), Number(width) + diff));
const adjustedDiff = newWidth - Number(width);
this.activeDivider.interactiveWidth = newWidth;
// Give immediate visual feedback
this.activeDivider.style.transform = `translateX(${adjustedDiff}px)`;
}
onDividerUp() {
const { index } = this.activeDivider.dataset;
// Store new width on the field data
if (this.activeDivider.interactiveWidth) {
this.fields[Number(index)].width = this.activeDivider.interactiveWidth;
}
// Reset visual feedback
this.activeDivider.style.transform = `translateX(0px)`;
window.removeEventListener('mousemove', this.onDividerMove);
window.removeEventListener('touchmove', this.onDividerMove);
window.removeEventListener('mouseup', this.onDividerUp);
window.removeEventListener('touchend', this.onDividerUp);
// Update column stretching before rendering
this.needsColumnResize = true;
this.updateColumnStretching();
// Render to apply change
this.forceRender++;
}
// Column visibility toggle handlers
toggleVisibilityMenu(e) {
e.preventDefault();
// TODO: replace this with contextual menu component, when available
const visibilityToggle = this.hostElement.shadowRoot.querySelector('.visibility-toggle');
const menu = visibilityToggle.children[1];
// By default
if (visibilityToggle.style.display === 'none') {
visibilityToggle.style.display = 'block';
menu.style.transform = `translate(${e.clientX}px, ${e.clientY}px)`;
}
else {
visibilityToggle.style.display = 'none';
}
}
toggleColumnVisibility(value, columnIndex) {
this.fields[columnIndex].visible = value;
this.forceRender++;
// Update column stretching
this.needsColumnResize = true;
this.updateColumnStretching();
}
// Resize handlers
addResizeObserver() {
if (!resizeObserver) {
// @ts-ignore
resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
// Skip if table not visible/attached
if (entry.target.offsetParent === null) {
return;
}
entry.target.applyResponsiveClasses(entry);
entry.target.updateColumnStretching();
}
});
}
this.elMmainContainer = this.hostElement.shadowRoot.querySelector(`.${name}`);
// Add this instance's callbacks, as resizeObserver is reused
this.elMmainContainer.applyResponsiveClasses = this.applyResponsiveClasses;
this.elMmainContainer.updateColumnStretching = this.updateColumnStretching;
resizeObserver.observe(this.elMmainContainer);
}
removeResizeObserver() {
if (this.elMmainContainer) {
resizeObserver.unobserve(this.elMmainContainer);
}
}
applyResponsiveClasses() {
// Apply container-scoped media-query-style classes
const newIsMobile = this.elMmainContainer.offsetWidth <= 500;
if (this.isMobile !== newIsMobile) {
this.forceRender++;
}
this.isMobile = newIsMobile;
}
updateColumnStretching() {
// NOTE: any styling padding/margin width changes need to be adjusted here as well
// Ignore auto-width-check content renders
if (this.needsAutoWidthParse) {
return;
}
const container = this.elMmainContainer;
// Minus 2 for border
const containerWidth = container.offsetWidth - 2;
const hasContainerWidthChanged = this.lastContainerWidth !== containerWidth;
// If width hasn't changed, don't re-calculate
if (!hasContainerWidthChanged && !this.needsColumnResize) {
return;
}
this.needsColumnResize = false;
this.lastContainerWidth = containerWidth;
// Don't calculate when mobile layout
if (container.offsetWidth <= 500) {
return;
}
// The theoretical target width - ignoring any previously applied stretching
const targetContentWidth = (() => {
let total = 0;
// Extra margin on first column
total += 8;
if (this.numbered) {
// 32 for padding+margin
total += this.numberColumnWidth + 32;
// this.selectionColumnWidth;
}
if (this.selectable) {
// 32 for padding+margin
total += this.selectionColumnWidth + 32;
// If both selectable and numbered - adjust for reduced margin between
if (this.numbered) {
total -= 16;
}
}
// Add each visible column's target width
this.fields.forEach(({ visible = true, width }) => {
if (visible) {
// 32 for padding+margin
total += width + 32;
}
});
return total;
})();
// Update value passed to nested content to overcome table display layout
this.contentWidth = Math.max(targetContentWidth, containerWidth);
const diff = containerWidth - targetContentWidth;
if (diff <= 0) {
// content larger than container (scrollbar), remove all stretching
this.fields.forEach((field) => (field.stretchWidth = 0));
}
else {
// container larger than content (gap to the right), calculate stretching
// If stretchWeight set, divide value between total to get final weight
// If stretchWeight unset, share remainder of 1 (if any) between all unset cols
let totalSetWeight = 0;
let unsetColsCount = 0;
this.fields.forEach(({ visible = true, stretchWeight }) => {
// Disregard invisible columns
if (!visible) {
return;
}
if (typeof stretchWeight === 'number') {
totalSetWeight += stretchWeight;
}
else {
unsetColsCount++;
}
});
const remainderWeight = Math.max(0, 1 - totalSetWeight);
// Set total to be divided against to be above 1 to keep total set/unset weights equal to 1
totalSetWeight = Math.max(1, totalSetWeight);
this.fields.forEach((field) => {
const { visible = true, stretchWeight } = field;
if (!visible) {
return;
}
// Actual stretch weight, out of a total 1 for all columns
let weight = 0;
if (typeof stretchWeight === 'number') {
weight = stretchWeight / totalSetWeight;
}
else if (remainderWeight > 0) {
weight = remainderWeight / unsetColsCount;
}
// Apply stretching with the weight percentage
field.stretchWidth = diff * weight;
});
}
this.forceRender++;
}
// Auto column width handlers
calculateAutoWidths() {
let isVisible = false;
const columns = this.hostElement.shadowRoot.querySelectorAll(`.${name}__auto-width-check td`);
columns.forEach((cell) => {
// Make sure table is actually rendered (eg not display:none etc)
if (!isVisible && cell.offsetParent !== null) {
isVisible = true;
}
if (!isVisible) {
return;
}
// Update field width with that of largest content
this.fields[cell.dataset.columnindex].width = cell.clientWidth;
});
if (!isVisible) {
return;
}
// Wrap in setTimeout to avoid warning about forcing render within render callback
setTimeout(() => {
this.needsAutoWidthParse = false;
this.forceRender++;
});
}
// Event triggers
triggerSortEvent(sortDirection, type, columnIndex) {
const data = {
rows: this.rows,
type,
sortDirection,
columnIndex,
};
emitEvent(this, 'scaleSort', data);
}
triggerEditEvent(value, rowIndex, columnIndex) {
const data = {
rows: this.rows,
rowIndex,
columnIndex,
value,
};
emitEvent(this, 'scaleEdit', data);
// Force render for checkboxes
this.forceRender++;
}
onTableScroll() {
if (!this.freezeHeader || this.hideHeader) {
return;
}
// Freeze header
const scrollY = this.elScrollContainer.scrollTop;
this.elTableHead.style.transform = `translateY(${scrollY}px)`;
}
renderSettingsMenu() {
var _a, _b, _c;
return (h("scale-menu-flyout", { class: `${name}__settings-menu` }, h("scale-button", { slot: "trigger", variant: "secondary", "icon-only": true, "data-sortable": this.isSortable }, h("scale-icon-service-settings", { accessibilityTitle: "Table options" })), h("scale-menu-flyout-list", null, this.isSortable && (h("scale-menu-flyout-item", { id: "sortBy", onClick: this.handleMenuListClick }, h("scale-icon-action-sort", { slot: "prefix" }), ((_a = this.localization) === null || _a === void 0 ? void 0 : _a.sortBy) || 'Sort By', h("scale-menu-flyout-list", { slot: "sublist", id: "sortByList" }, this.fields.map(({ label, type, sortable, sortDirection = 'none' }, columnIndex) => {
if (!sortable) {
return '';
}
return (h("scale-menu-flyout-item", { "onScale-select": () => this.toggleTableSorting(sortDirection, columnIndex, type) }, sortDirection === 'ascending' && (h("scale-icon-navigation-collapse-up", { size: 16, slot: "prefix" })), sortDirection === 'descending' && (h("scale-icon-navigation-collapse-down", { size: 16, slot: "prefix" })), sortDirection === 'none' && (h("scale-icon-navigation-collapse-up", { size: 16, slot: "prefix", style: { opacity: '0' } })), label || type));
})))), h("scale-menu-flyout-item", { id: "toggleVisibility", onClick: this.handleMenuListClick }, h("scale-icon-action-hide-password", { slot: "prefix" }), ((_b = this.localization) === null || _b === void 0 ? void 0 : _b.toggle) || 'Toggle Visibility', h("scale-menu-flyout-list", { slot: "sublist", "close-on-select": "false", id: "toggl