UNPKG

@telekom/scale-components

Version:

Scale is the digital design system for Telekom products and experiences.

1,102 lines (1,085 loc) 60.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const index = require('./index-a0ea3d79.js'); const index$1 = require('./index-53f5a5fc.js'); const utils = require('./utils-e9c3b953.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 index.h("scale-switch", Object.assign({ size: "small" }, props)); default: // 'checkbox' return index.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 index.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 index.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 (index.h("div", { class: `tbody__bar-cell` }, index.h("scale-progress-bar", { class: "data-grid-progress-bar", "aria-hidden": "true", percentage: progress, // showStatus={true} mute: true }), index.h("p", { class: `scl-body` }, content))); default: // progress return (index.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 (index.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 && (index.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 ? (index.h("scale-icon-navigation-collapse-up", { size: 14 })) : (index.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 (index.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 (index.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 (index.h("scale-dropdown-select", Object.assign({ floatingStrategy: "fixed" }, props), options.map((option) => { return (index.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 (index.h("ul", { class: `tbody__tag-list` }, tags.map((tag) => (index.h("li", null, index.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 index.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 (index.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 index.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 (index.h("div", { class: `tbody__text-cell` }, iconPrefix && (index.h("span", { class: `tbody__text-cell-prefix` }, index.h(`scale-icon-${iconPrefix}`))), index.h("p", { class: `scl-${variant}` }, value), iconSuffix && (index.h("span", { class: `tbody__text-cell-suffix` }, index.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 (index.h("div", { class: `tbody__actions` }, content.map((action) => { const { label } = action, props = __rest(action, ["label"]); if (typeof label === 'object' && '__html' in label) { return (index.h("scale-button", Object.assign({ size: "small", innerHTML: label.__html }, props))); } return (index.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 = class { /* 6. Lifecycle Events (call order) */ constructor(hostRef) { index.registerInstance(this, hostRef); this.scaleEdit = index.createEvent(this, "scale-edit", 7); this.scaleEditLegacy = index.createEvent(this, "scaleEdit", 7); this.scaleSort = index.createEvent(this, "scale-sort", 7); this.scaleSortLegacy = index.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 index$1.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, }; utils.emitEvent(this, 'scaleSort', data); } triggerEditEvent(value, rowIndex, columnIndex) { const data = { rows: this.rows, rowIndex, columnIndex, value, }; utils.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 (index.h("scale-menu-flyout", { class: `${name}__settings-menu` }, index.h("scale-button", { slot: "trigger", variant: "secondary", "icon-only": true, "data-sortable": this.isSortable }, index.h("scale-icon-service-settings", { accessibilityTitle: "Table options" })), index.h("scale-menu-flyout-list", null, this.isSortable && (index.h("scale-menu-flyout-item", { id: "sortBy", onClick: this.handleMenuListClick }, index.h("scale-icon-action-sort", { slot: "prefix" }), ((_a = this.localization) === null || _a === void 0 ? void 0 : _a.sortBy) || 'Sort By', index.h("scale-menu-flyout-list", { slot: "sublist", id: "sortByList" }, this.fields.map(({ label, type, sortable, sortDirection = 'none' }, columnIndex) => { if (!sortable) { return ''; } return (index.h("scale-menu-flyout-item", { "onScale-select": () => this.toggleTableSorting(sortDirection, columnIndex, type) }, sortDirection === 'ascending' && (index.h("scale-icon-navigation-collapse-up", { size: 16, slot: "prefix" })), sortDirection === 'descending' && (index.h("scale-icon-navigation-collapse-down", { size: 16, slot: "prefix" })), sortDirection === 'none' && (index.h("scale-icon-navigation-collapse-up", { size: 16, slot: "prefix", style: { opacity: '0' } })), label || type)); })))), index.h("scale-menu-flyout-item", { id: "toggleVisibility", onClick: this.handleMenuListClick }, index.h("scale-icon-action-hide-password", { slot: "prefix" }), ((_b = this.localization) === null || _b === void 0 ? void 0 : _b.toggle) || 'Toggle Visibility', index.h("scale-menu-flyout-list", { slot: "sublist", "close-on-select": "false", id: "toggleVisibilityList" }, this.fields.map(({ label, type, visible = CELL_TYPES[type].defaults.visible !== undefined ? CELL_TYPES[type].defaults.visible : CELL_DEFAULTS.visible, }, columnIndex) => { return (index.h("scale-menu-flyout-item", { checkable: "checkbox", checked: !!visible, "onScale-select": () => this.toggleColumnVisibility(!visible, columnIndex) }, label || type)); }))), this.selectable && (index.h("scale-menu-flyout-item", { "onScale-select": () => { this.elToggleSelectAll.checked = !this.elToggleSelectAll.checked; this.toggleSelectAll(); } }, index.h("scale-icon", { slot: "prefix", path: "M20.9328 10.6668C20.5132 10.6668 20.1731 11.0069 20.1731 11.4265V20.3269H1.5194V1.67309H16.5049C16.9245 1.67309 17.2646 1.33292 17.2646 0.913386C17.2646 0.49385 16.9245 0.153687 16.5049 0.153687H0.759699C0.340163 0.153687 0 0.49385 0 0.913386V21.0866C0 21.5062 0.340163 21.8463 0.759699 21.8463H20.9328C21.3523 21.8463 21.6925 21.5062 21.6925 21.0866V11.4265C21.6925 11.0069 21.3524 10.6668 20.9328 10.6668ZM23.7774 0.653387C23.4807 0.356739 22.9997 0.356739 22.703 0.653387L10.3293 13.0272L7.25501 9.9529C6.9583 9.65625 6.47732 9.65625 6.18061 9.9529C5.88396 10.2496 5.88396 10.7306 6.18061 11.0273L9.7921 14.6388C9.94045 14.7871 10.1349 14.8613 10.3293 14.8613C10.5237 14.8613 10.7181 14.7871 10.8665 14.6388L23.7774 1.72778C24.0741 1.43108 24.0741 0.950095 23.7774 0.653387Z" }), ((_c = this.localization) === null || _c === void 0 ? void 0 : _c.select) || 'Select / Deselect All')), index.h("slot", { name: "menu" })))); } renderTable() { if (this.needsAutoWidthParse) { return this.renderAutoWidthCheck(); } return (index.h("div", { ref: (el) => (this.elScrollContainer = el), class: `${name}__scroll-container`, style: { height: this.height || 'auto' }, onScroll: (