@ni/nimble-components 27.2.3 → 28.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/all-components-bundle.js +959 -956
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +94 -94
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/all-components.d.ts +1 -1
  6. package/dist/esm/all-components.js +1 -1
  7. package/dist/esm/all-components.js.map +1 -1
  8. package/dist/esm/mapping/icon/index.d.ts +1 -1
  9. package/dist/esm/mapping/icon/index.js +1 -1
  10. package/dist/esm/mapping/icon/index.js.map +1 -1
  11. package/dist/esm/mapping/spinner/index.d.ts +1 -1
  12. package/dist/esm/mapping/spinner/index.js +1 -1
  13. package/dist/esm/mapping/spinner/index.js.map +1 -1
  14. package/dist/esm/mapping/text/index.d.ts +1 -1
  15. package/dist/esm/mapping/text/index.js +1 -1
  16. package/dist/esm/mapping/text/index.js.map +1 -1
  17. package/dist/esm/patterns/dropdown/styles.js +2 -2
  18. package/dist/esm/patterns/dropdown/styles.js.map +1 -1
  19. package/dist/esm/table/testing/table.pageobject.d.ts +2 -2
  20. package/dist/esm/table/testing/table.pageobject.js +3 -3
  21. package/dist/esm/table/testing/table.pageobject.js.map +1 -1
  22. package/dist/esm/table-column/enum-base/types.d.ts +1 -1
  23. package/dist/esm/table-column/enum-base/types.js +1 -1
  24. package/dist/esm/table-column/enum-base/types.js.map +1 -1
  25. package/dist/esm/table-column/{icon → mapping}/cell-view/index.d.ts +4 -4
  26. package/dist/esm/table-column/{icon → mapping}/cell-view/index.js +11 -11
  27. package/dist/esm/table-column/mapping/cell-view/index.js.map +1 -0
  28. package/dist/esm/table-column/mapping/cell-view/styles.js.map +1 -0
  29. package/dist/esm/table-column/mapping/cell-view/template.d.ts +2 -0
  30. package/dist/esm/table-column/mapping/cell-view/template.js.map +1 -0
  31. package/dist/esm/table-column/{icon → mapping}/group-header-view/index.d.ts +4 -4
  32. package/dist/esm/table-column/{icon → mapping}/group-header-view/index.js +10 -8
  33. package/dist/esm/table-column/mapping/group-header-view/index.js.map +1 -0
  34. package/dist/esm/table-column/mapping/group-header-view/styles.js.map +1 -0
  35. package/dist/esm/table-column/mapping/group-header-view/template.d.ts +2 -0
  36. package/dist/esm/table-column/mapping/group-header-view/template.js.map +1 -0
  37. package/dist/esm/table-column/{icon → mapping}/index.d.ts +9 -8
  38. package/dist/esm/table-column/{icon → mapping}/index.js +14 -13
  39. package/dist/esm/table-column/mapping/index.js.map +1 -0
  40. package/dist/esm/table-column/{icon/models/table-column-icon-validator.d.ts → mapping/models/table-column-mapping-validator.d.ts} +3 -3
  41. package/dist/esm/table-column/{icon/models/table-column-icon-validator.js → mapping/models/table-column-mapping-validator.js} +9 -9
  42. package/dist/esm/table-column/mapping/models/table-column-mapping-validator.js.map +1 -0
  43. package/dist/esm/table-column/{icon/testing/table-column-icon.pageobject.d.ts → mapping/testing/table-column-mapping.pageobject.d.ts} +2 -2
  44. package/dist/esm/table-column/{icon/testing/table-column-icon.pageobject.js → mapping/testing/table-column-mapping.pageobject.js} +3 -3
  45. package/dist/esm/table-column/mapping/testing/table-column-mapping.pageobject.js.map +1 -0
  46. package/dist/esm/table-column/{icon → mapping}/types.d.ts +1 -1
  47. package/dist/esm/table-column/{icon → mapping}/types.js +1 -1
  48. package/dist/esm/table-column/mapping/types.js.map +1 -0
  49. package/package.json +3 -3
  50. package/dist/esm/table-column/icon/cell-view/index.js.map +0 -1
  51. package/dist/esm/table-column/icon/cell-view/styles.js.map +0 -1
  52. package/dist/esm/table-column/icon/cell-view/template.d.ts +0 -2
  53. package/dist/esm/table-column/icon/cell-view/template.js.map +0 -1
  54. package/dist/esm/table-column/icon/group-header-view/index.js.map +0 -1
  55. package/dist/esm/table-column/icon/group-header-view/styles.js.map +0 -1
  56. package/dist/esm/table-column/icon/group-header-view/template.d.ts +0 -2
  57. package/dist/esm/table-column/icon/group-header-view/template.js.map +0 -1
  58. package/dist/esm/table-column/icon/index.js.map +0 -1
  59. package/dist/esm/table-column/icon/models/table-column-icon-validator.js.map +0 -1
  60. package/dist/esm/table-column/icon/testing/table-column-icon.pageobject.js.map +0 -1
  61. package/dist/esm/table-column/icon/types.js.map +0 -1
  62. /package/dist/esm/table-column/{icon → mapping}/cell-view/styles.d.ts +0 -0
  63. /package/dist/esm/table-column/{icon → mapping}/cell-view/styles.js +0 -0
  64. /package/dist/esm/table-column/{icon → mapping}/cell-view/template.js +0 -0
  65. /package/dist/esm/table-column/{icon → mapping}/group-header-view/styles.d.ts +0 -0
  66. /package/dist/esm/table-column/{icon → mapping}/group-header-view/styles.js +0 -0
  67. /package/dist/esm/table-column/{icon → mapping}/group-header-view/template.js +0 -0
@@ -16333,7 +16333,7 @@
16333
16333
 
16334
16334
  /**
16335
16335
  * Do not edit directly
16336
- * Generated on Thu, 02 May 2024 18:40:05 GMT
16336
+ * Generated on Tue, 07 May 2024 13:04:38 GMT
16337
16337
  */
16338
16338
 
16339
16339
  const Information100DarkUi = "#a46eff";
@@ -20588,7 +20588,7 @@
20588
20588
  position: relative;
20589
20589
  justify-content: center;
20590
20590
  ${userSelectNone}
20591
- min-width: 250px;
20591
+ min-width: ${menuMinWidth};
20592
20592
  outline: none;
20593
20593
  vertical-align: top;
20594
20594
  --ni-private-hover-indicator-width: calc(${borderWidth} + 1px);
@@ -24915,7 +24915,7 @@
24915
24915
 
24916
24916
  /**
24917
24917
  * Defines a mapping from one data value ('key' property) to display text ('text' property).
24918
- * One or more may be added as children of a nimble-table-column-icon element to define
24918
+ * One or more may be added as children of a nimble-table-column-mapping element to define
24919
24919
  * how a specific data value should be displayed as text in that column's cells.
24920
24920
  */
24921
24921
  class MappingText extends Mapping$1 {
@@ -24934,7 +24934,7 @@
24934
24934
  }
24935
24935
  /**
24936
24936
  * Maps a data value to an icon.
24937
- * One or more may be added as children of a nimble-table-column-icon element to define
24937
+ * One or more may be added as children of a nimble-table-column-mapping element to define
24938
24938
  * how specific data values should be displayed as icons in that column's cells.
24939
24939
  */
24940
24940
  class MappingIcon extends Mapping$1 {
@@ -25001,7 +25001,7 @@
25001
25001
 
25002
25002
  /**
25003
25003
  * Maps data values to a spinner.
25004
- * One or more may be added as children of a nimble-table-column-icon element to define
25004
+ * One or more may be added as children of a nimble-table-column-mapping element to define
25005
25005
  * which specific data values should be displayed as spinners in that column's cells.
25006
25006
  */
25007
25007
  class MappingSpinner extends Mapping$1 {
@@ -68847,1152 +68847,1155 @@ img.ProseMirror-separator {
68847
68847
  .withPrefix('nimble')
68848
68848
  .register(nimbleTableColumnDurationText());
68849
68849
 
68850
- // Avoiding a wrapping <template> and be careful about starting and ending whitespace
68851
- // so the template can be composed into other column header templates
68852
- // prettier-ignore
68853
- const template$9 = html `<span
68854
- ${overflow('hasOverflow')}
68855
- class="header-content"
68856
- title=${x => (x.hasOverflow && x.headerTextContent ? x.headerTextContent : null)}
68857
- >
68858
- <slot ${ref('contentSlot')}></slot>
68859
- <slot ${slotted({ property: 'unitElements', filter: elements() })} name="unit"></slot>
68860
- </span>`;
68861
-
68862
- /**
68863
- * The group header view for displaying number fields as text.
68864
- */
68865
- class TableColumnNumberTextGroupHeaderView extends TableColumnTextGroupHeaderViewBase {
68866
- updateText() {
68867
- this.text = this.columnConfig?.formatter?.format(this.groupHeaderValue) ?? '';
68868
- }
68869
- }
68870
- const tableColumnNumberTextGroupHeaderView = TableColumnNumberTextGroupHeaderView.compose({
68871
- baseName: 'table-column-number-text-group-header-view',
68872
- template: template$b,
68873
- styles: styles$c
68874
- });
68875
- DesignSystem.getOrCreate()
68876
- .withPrefix('nimble')
68877
- .register(tableColumnNumberTextGroupHeaderView());
68878
- const tableColumnNumberTextGroupHeaderTag = 'nimble-table-column-number-text-group-header-view';
68879
-
68880
68850
  /**
68881
- * A cell view for displaying number fields as text
68851
+ * Converts a Mapping key (which is a string when configured in HTML) to the
68852
+ * given keyType. The converted value can then be used to compare against
68853
+ * values in the table data.
68882
68854
  */
68883
- class TableColumnNumberTextCellView extends TableColumnTextCellViewBase {
68884
- columnConfigChanged() {
68885
- super.columnConfigChanged();
68886
- this.alignment = this.columnConfig?.alignment ?? TextCellViewBaseAlignment.left;
68855
+ const resolveKeyWithType = (key, keyType) => {
68856
+ if (keyType === 'number') {
68857
+ const converted = nullableNumberConverter.fromView(key);
68858
+ return converted === null ? undefined : converted;
68887
68859
  }
68888
- updateText() {
68889
- this.text = this.columnConfig?.formatter?.format(this.cellRecord?.value) ?? '';
68860
+ if (keyType === 'boolean') {
68861
+ if (key === false || key === 'false') {
68862
+ return false;
68863
+ }
68864
+ if (key === true || key === 'true') {
68865
+ return true;
68866
+ }
68867
+ return undefined;
68890
68868
  }
68891
- }
68892
- const numberTextCellView = TableColumnNumberTextCellView.compose({
68893
- baseName: 'table-column-number-text-cell-view',
68894
- template: template$a,
68895
- styles: styles$b
68896
- });
68897
- DesignSystem.getOrCreate().withPrefix('nimble').register(numberTextCellView());
68898
- const tableColumnNumberTextCellViewTag = 'nimble-table-column-number-text-cell-view';
68899
-
68900
- /**
68901
- * Formatting scheme for the number-text table column
68902
- */
68903
- const NumberTextFormat = {
68904
- default: undefined,
68905
- decimal: 'decimal'
68906
- };
68907
- /**
68908
- * The aligment of the value in the number-text table column.
68909
- * The `default` alignment is determined by the column's `NumberTextFormat`.
68910
- */
68911
- const NumberTextAlignment = {
68912
- default: undefined,
68913
- left: 'left',
68914
- right: 'right'
68869
+ return key?.toString() ?? undefined;
68915
68870
  };
68916
68871
 
68917
68872
  /**
68918
- * The base class for unit formats.
68873
+ * Base class for table columns that map values to content
68919
68874
  */
68920
- class UnitFormat {
68875
+ class TableColumnEnumBase extends TableColumn {
68876
+ constructor() {
68877
+ super(...arguments);
68878
+ /** @internal */
68879
+ this.mappingNotifiers = [];
68880
+ /** @internal */
68881
+ this.mappings = [];
68882
+ this.keyType = 'string';
68883
+ }
68921
68884
  /**
68922
- * Formats a number value to a string.
68923
- * For nullish values or values that result in an exception being thrown, empty string is returned
68885
+ * @internal
68886
+ *
68887
+ * Triggers a request to update the columnConfig when any observable property on
68888
+ * a mapping is updated.
68924
68889
  */
68925
- format(value) {
68926
- if (typeof value !== 'number') {
68927
- return '';
68928
- }
68929
- try {
68930
- return this.tryFormat(value);
68890
+ handleChange(source, args) {
68891
+ if (source instanceof Mapping$1 && typeof args === 'string') {
68892
+ this.updateColumnConfig();
68931
68893
  }
68932
- catch {
68933
- return '';
68894
+ }
68895
+ /**
68896
+ * Called when any Mapping related state has changed.
68897
+ */
68898
+ updateColumnConfig() {
68899
+ this.columnInternals.validator.validate(this.mappings, this.keyType);
68900
+ this.columnInternals.columnConfig = this.checkValidity()
68901
+ ? this.createColumnConfig(this.getMappingConfigs())
68902
+ : undefined;
68903
+ }
68904
+ getMappingConfigs() {
68905
+ const mappingConfigs = new Map();
68906
+ this.mappings.forEach(mapping => {
68907
+ const key = resolveKeyWithType(mapping.key, this.keyType);
68908
+ if (key === undefined) {
68909
+ throw Error('Key was invalid for type. Validation should have prevented this.');
68910
+ }
68911
+ const mappingConfig = this.createMappingConfig(mapping);
68912
+ mappingConfigs.set(key, mappingConfig);
68913
+ });
68914
+ return mappingConfigs;
68915
+ }
68916
+ fieldNameChanged() {
68917
+ this.columnInternals.dataRecordFieldNames = [this.fieldName];
68918
+ this.columnInternals.operandDataRecordFieldName = this.fieldName;
68919
+ }
68920
+ mappingsChanged() {
68921
+ this.updateColumnConfig();
68922
+ this.observeMappings();
68923
+ }
68924
+ keyTypeChanged() {
68925
+ this.updateColumnConfig();
68926
+ }
68927
+ removeMappingObservers() {
68928
+ this.mappingNotifiers.forEach(notifier => {
68929
+ notifier.unsubscribe(this);
68930
+ });
68931
+ this.mappingNotifiers = [];
68932
+ }
68933
+ observeMappings() {
68934
+ this.removeMappingObservers();
68935
+ for (const mapping of this.mappings) {
68936
+ const notifier = Observable.getNotifier(mapping);
68937
+ notifier.subscribe(this);
68938
+ this.mappingNotifiers.push(notifier);
68934
68939
  }
68935
68940
  }
68936
68941
  }
68942
+ __decorate$1([
68943
+ observable
68944
+ ], TableColumnEnumBase.prototype, "mappings", void 0);
68945
+ __decorate$1([
68946
+ attr({ attribute: 'field-name' })
68947
+ ], TableColumnEnumBase.prototype, "fieldName", void 0);
68948
+ __decorate$1([
68949
+ attr({ attribute: 'key-type' })
68950
+ ], TableColumnEnumBase.prototype, "keyType", void 0);
68937
68951
 
68952
+ const styles$a = css `
68953
+ ${styles$e}
68954
+
68955
+ slot[name='mapping'] {
68956
+ display: none;
68957
+ }
68958
+ `;
68959
+
68960
+ const template$9 = html `${template$d}<slot ${slotted('mappings')} name="mapping"></slot>`;
68961
+
68962
+ const enumBaseValidityFlagNames = [
68963
+ 'invalidMappingKeyValueForType',
68964
+ 'duplicateMappingKey',
68965
+ 'missingKeyValue'
68966
+ ];
68938
68967
  /**
68939
- * A class that knows how to format a numeric value as a string that includes units.
68968
+ * Validator base class for table columns containing mappings. Implementations MUST include enumBaseValidityFlagNames in validity flag names set.
68940
68969
  */
68941
- class ScaledUnitFormat {
68942
- constructor(scaledUnitFormatFactoryOptions) {
68943
- this.locale = scaledUnitFormatFactoryOptions.locale;
68944
- this.intlNumberFormatOptions = scaledUnitFormatFactoryOptions.intlNumberFormatOptions;
68970
+ class TableColumnEnumBaseValidator extends ColumnValidator {
68971
+ constructor(configValidityKeys) {
68972
+ super(configValidityKeys);
68973
+ }
68974
+ validate(mappings, keyType) {
68975
+ this.untrackAll();
68976
+ const keys = mappings.map(mapping => mapping.key);
68977
+ this.validateKeyValuesForType(keys, keyType);
68978
+ this.validateUniqueKeys(keys, keyType);
68979
+ this.validateNoMissingKeys(mappings);
68980
+ }
68981
+ validateKeyValuesForType(keys, keyType) {
68982
+ // Ignore undefined keys, because validateNoMissingKeys covers that case.
68983
+ // We should only set 'invalidMappingKeyValueForType' when there is a key,
68984
+ // but it isn't appropriate for the type.
68985
+ const invalid = keys.some(key => key !== undefined
68986
+ && resolveKeyWithType(key, keyType) === undefined);
68987
+ this.setConditionValue('invalidMappingKeyValueForType', invalid);
68988
+ }
68989
+ validateUniqueKeys(keys, keyType) {
68990
+ const typedKeys = keys.map(x => resolveKeyWithType(x, keyType));
68991
+ const invalid = new Set(typedKeys).size !== typedKeys.length;
68992
+ this.setConditionValue('duplicateMappingKey', invalid);
68993
+ }
68994
+ validateNoMissingKeys(mappings) {
68995
+ const invalid = mappings.some(mapping => mapping.key === undefined);
68996
+ this.setConditionValue('missingKeyValue', invalid);
68945
68997
  }
68946
68998
  }
68947
68999
 
69000
+ const mappingColumnValidityFlagNames = [
69001
+ ...enumBaseValidityFlagNames,
69002
+ 'unsupportedMappingType',
69003
+ 'invalidIconName',
69004
+ 'missingTextValue'
69005
+ ];
68948
69006
  /**
68949
- * A formatter for units that can be formatted/translated by Intl.NumberFormat
69007
+ * Validator for TableColumnMapping
68950
69008
  */
68951
- class IntlNumberFormatScaledUnitFormat extends ScaledUnitFormat {
68952
- constructor(scaledUnitFormatFactoryOptions, unitSpecificIntlNumberFormatOptions) {
68953
- super(scaledUnitFormatFactoryOptions);
68954
- this.formatter = new Intl.NumberFormat(this.locale, {
68955
- ...unitSpecificIntlNumberFormatOptions,
68956
- // Application configured options override unit specific options
68957
- ...this.intlNumberFormatOptions
68958
- });
69009
+ class TableColumnMappingValidator extends TableColumnEnumBaseValidator {
69010
+ constructor() {
69011
+ super(mappingColumnValidityFlagNames);
68959
69012
  }
68960
- static createFactory(unitSpecificIntlNumberFormatOptions) {
68961
- return (scaledUnitFormatFactoryOptions) => new IntlNumberFormatScaledUnitFormat(scaledUnitFormatFactoryOptions, unitSpecificIntlNumberFormatOptions);
69013
+ static isIconMappingElement(mapping) {
69014
+ return mapping instanceof MappingIcon;
68962
69015
  }
68963
- format(value) {
68964
- return this.formatter.format(value);
69016
+ static isSupportedMappingElement(mapping) {
69017
+ return (mapping instanceof MappingIcon
69018
+ || mapping instanceof MappingSpinner
69019
+ || mapping instanceof MappingText);
69020
+ }
69021
+ static hasUnresolvedIcon(mappingIcon) {
69022
+ return (typeof mappingIcon.icon === 'string'
69023
+ && mappingIcon.resolvedIcon === undefined);
69024
+ }
69025
+ validate(mappings, keyType) {
69026
+ super.validate(mappings, keyType);
69027
+ this.validateMappingTypes(mappings);
69028
+ this.validateIconNames(mappings);
69029
+ this.validateNoMissingText(mappings);
69030
+ }
69031
+ validateIconNames(mappings) {
69032
+ const invalid = mappings
69033
+ .filter(TableColumnMappingValidator.isIconMappingElement)
69034
+ .some(TableColumnMappingValidator.hasUnresolvedIcon);
69035
+ this.setConditionValue('invalidIconName', invalid);
69036
+ }
69037
+ validateNoMissingText(mappings) {
69038
+ const invalid = mappings
69039
+ .filter(TableColumnMappingValidator.isSupportedMappingElement)
69040
+ .some(mapping => mapping.text === undefined);
69041
+ this.setConditionValue('missingTextValue', invalid);
69042
+ }
69043
+ validateMappingTypes(mappings) {
69044
+ const valid = mappings.every(TableColumnMappingValidator.isSupportedMappingElement);
69045
+ this.setConditionValue('unsupportedMappingType', !valid);
68965
69046
  }
68966
69047
  }
68967
69048
 
69049
+ const styles$9 = css `
69050
+ ${display('inline-flex')}
69051
+
69052
+ :host {
69053
+ gap: ${smallPadding};
69054
+ align-items: center;
69055
+ }
69056
+
69057
+ .reserve-icon-size {
69058
+ flex-shrink: 0;
69059
+ width: ${iconSize};
69060
+ height: ${iconSize};
69061
+ }
69062
+
69063
+ .text {
69064
+ flex-shrink: 1;
69065
+ font: ${bodyFont};
69066
+ color: ${bodyFontColor};
69067
+ white-space: nowrap;
69068
+ overflow: hidden;
69069
+ text-overflow: ellipsis;
69070
+ }
69071
+ `;
69072
+
69073
+ // prettier-ignore
69074
+ const template$8 = html `
69075
+ ${when(x => x.visualizationTemplate, html `
69076
+ <span class="reserve-icon-size">
69077
+ ${x => x.visualizationTemplate}
69078
+ </span>
69079
+ `)}
69080
+ <span
69081
+ ${overflow('hasOverflow')}
69082
+ title="${x => (x.hasOverflow && x.text ? x.text : null)}"
69083
+ class="text"
69084
+ >${x => x.text}</span>
69085
+ `;
69086
+
68968
69087
  /**
68969
- * A unit that represents a scaled version of a base unit.
69088
+ * Predefined icon appearance states
69089
+ * @public
68970
69090
  */
68971
- class ScaledUnit {
68972
- constructor(scaleFactor, scaledUnitFormatFactory) {
68973
- this.scaleFactor = scaleFactor;
68974
- this.scaledUnitFormatFactory = scaledUnitFormatFactory;
68975
- }
68976
- isBase() {
68977
- return this.scaleFactor === 1;
68978
- }
68979
- }
69091
+ const IconSeverity = {
69092
+ default: undefined,
69093
+ error: 'error',
69094
+ warning: 'warning',
69095
+ success: 'success',
69096
+ information: 'information'
69097
+ };
68980
69098
 
68981
69099
  /**
68982
- * A unit scale consisting of a set of scaled units.
69100
+ * Common state shared across Mapping Config
68983
69101
  */
68984
- class UnitScale {
68985
- constructor(supportedScaledUnits) {
68986
- this.supportedScaledUnits = supportedScaledUnits;
68987
- const unitsSorted = supportedScaledUnits.every((curr, i, arr) => i === 0 || arr[i - 1].scaleFactor < curr.scaleFactor);
68988
- if (!unitsSorted) {
68989
- throw new Error('Supported scaled units must have unique and ordered scale factors');
68990
- }
68991
- const baseScaledUnit = supportedScaledUnits.find(x => x.isBase());
68992
- if (!baseScaledUnit) {
68993
- throw new Error('Supported scaled units must include a base scaled unit (scale factor=1)');
68994
- }
68995
- this.supportedScaledUnits = supportedScaledUnits;
68996
- this.baseScaledUnit = baseScaledUnit;
68997
- }
68998
- // Note that for the sake of reducing complexity in the implementation,
68999
- // we do NOT consider the effects of rounding when picking the unit to
69000
- // use for a given value. If formatting results in rounding, a value
69001
- // may be shown with an unexpected unit. Examples:
69002
- // - 999 bytes with two significant digits => "1000 bytes" (instead of "1 kB")
69003
- // - 0.00000000000000001 volts (= 0.01 fV) with one fractional digit => "0 fV" (instead of "0 volts")
69004
- scaleNumber(number) {
69005
- const magnitude = Math.abs(number);
69006
- const onlyBaseScaledUnit = this.supportedScaledUnits.length === 1;
69007
- if (onlyBaseScaledUnit
69008
- || magnitude === 0
69009
- || magnitude === Infinity
69010
- || Number.isNaN(magnitude)) {
69011
- return { scaledValue: number, scaledUnit: this.baseScaledUnit };
69012
- }
69013
- for (let i = this.supportedScaledUnits.length - 1; i >= 0; i -= 1) {
69014
- const scaledUnit = this.supportedScaledUnits[i];
69015
- if (magnitude / scaledUnit.scaleFactor >= 1) {
69016
- return {
69017
- scaledValue: number / scaledUnit.scaleFactor,
69018
- scaledUnit
69019
- };
69020
- }
69021
- }
69022
- const smallestUnit = this.supportedScaledUnits[0];
69023
- return {
69024
- scaledValue: number / smallestUnit.scaleFactor,
69025
- scaledUnit: smallestUnit
69026
- };
69102
+ class MappingConfig {
69103
+ constructor(text) {
69104
+ this.text = text;
69027
69105
  }
69028
69106
  }
69029
69107
 
69108
+ // Create an empty template containing only a space because creating a ViewTemplate
69109
+ // with an empty string throws an exception at runtime.
69110
+ // prettier-ignore
69111
+ const emptyTemplate = html ` `;
69112
+ const createIconTemplate = (icon) => {
69113
+ if (icon === undefined) {
69114
+ return emptyTemplate;
69115
+ }
69116
+ return html `
69117
+ <${icon}
69118
+ title="${x => (x.textHidden ? x.text : '')}"
69119
+ role="img"
69120
+ aria-label="${x => x.text}"
69121
+ aria-hidden="${x => (x.textHidden ? 'false' : 'true')}"
69122
+ severity="${x => x.severity}"
69123
+ >
69124
+ </${icon}>
69125
+ `;
69126
+ };
69030
69127
  /**
69031
- * Unit scale that is used to passthrough a number without applying scaling or units
69128
+ * Mapping configuration corresponding to a icon mapping
69032
69129
  */
69033
- class PassthroughUnitScale extends UnitScale {
69034
- constructor() {
69035
- super([
69036
- new ScaledUnit(10 ** 0, IntlNumberFormatScaledUnitFormat.createFactory({}))
69037
- ]);
69130
+ class MappingIconConfig extends MappingConfig {
69131
+ constructor(resolvedIcon, severity, text, textHidden) {
69132
+ super(text);
69133
+ this.severity = severity;
69134
+ this.textHidden = textHidden;
69135
+ this.iconTemplate = createIconTemplate(resolvedIcon);
69038
69136
  }
69039
69137
  }
69040
- const passthroughUnitScale = new PassthroughUnitScale();
69041
69138
 
69042
- // Workaround to avoid ts errors about signDisplay not accepting the value 'negative'.
69043
- // It has been supported by browsers since 8/23, but TypeScript still hasn't
69044
- // added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269
69045
- const signDisplay = 'negative';
69139
+ const spinnerTemplate = html `
69140
+ <${spinnerTag}
69141
+ title="${x => (x.textHidden ? x.text : '')}"
69142
+ aria-label="${x => x.text}"
69143
+ aria-hidden="${x => (x.textHidden ? 'false' : 'true')}"
69144
+ >
69145
+ </${spinnerTag}>
69146
+ `;
69046
69147
  /**
69047
- * Format for numbers with units to show in a tabular form.
69048
- * Large and tiny numbers are shown exponentially and the rest as decimal.
69148
+ * Mapping configuration corresponding to a spinner mapping
69049
69149
  */
69050
- class DefaultUnitFormat extends UnitFormat {
69051
- constructor(locale, { unitScale = passthroughUnitScale } = {
69052
- unitScale: passthroughUnitScale
69053
- }) {
69054
- super();
69055
- // Format options to use by default. It renders the number with a maximum of 6 signficant digits (including zero before decimal point).
69056
- this.defaultIntlNumberFormatOptions = {
69057
- maximumSignificantDigits: DefaultUnitFormat.maximumDigits,
69058
- maximumFractionDigits: DefaultUnitFormat.maximumDigits - 1,
69059
- roundingPriority: 'lessPrecision',
69060
- signDisplay
69061
- };
69062
- this.defaultScaledUnitFormatters = new Map();
69063
- // Format options for numbers that should be displayed in exponential notation. This should be used
69064
- // for numbers with magintudes over 'exponentialUpperBound' or under 'exponentialLowerBound'.
69065
- this.exponentialIntlNumberFormatOptions = {
69066
- maximumSignificantDigits: DefaultUnitFormat.maximumDigits,
69067
- notation: 'scientific',
69068
- signDisplay
69069
- };
69070
- for (const unit of unitScale.supportedScaledUnits) {
69071
- this.defaultScaledUnitFormatters.set(unit.scaleFactor, unit.scaledUnitFormatFactory({
69072
- locale,
69073
- intlNumberFormatOptions: this.defaultIntlNumberFormatOptions
69074
- }));
69075
- }
69076
- this.exponentialScaledUnitFormatter = unitScale.baseScaledUnit.scaledUnitFormatFactory({
69077
- locale,
69078
- intlNumberFormatOptions: this.exponentialIntlNumberFormatOptions
69079
- });
69080
- this.unitScale = unitScale;
69081
- }
69082
- resolvedOptions() {
69083
- return {
69084
- unitScale: this.unitScale
69085
- };
69086
- }
69087
- tryFormat(number) {
69088
- const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number);
69089
- const absoluteValue = Math.abs(scaledValue);
69090
- const useExponential = absoluteValue !== 0
69091
- && (absoluteValue >= DefaultUnitFormat.exponentialUpperBound
69092
- || absoluteValue < DefaultUnitFormat.exponentialLowerBound);
69093
- return useExponential
69094
- ? this.exponentialScaledUnitFormatter.format(number)
69095
- : this.defaultScaledUnitFormatters
69096
- .get(scaledUnit.scaleFactor)
69097
- .format(scaledValue);
69150
+ class MappingSpinnerConfig extends MappingConfig {
69151
+ constructor(text, textHidden) {
69152
+ super(text);
69153
+ this.textHidden = textHidden;
69154
+ this.spinnerTemplate = spinnerTemplate;
69098
69155
  }
69099
69156
  }
69100
- // The maximum number of digits that should be rendered for any given value.
69101
- DefaultUnitFormat.maximumDigits = 6;
69102
- // Use exponential notation for numbers that will be rendered with 3 leading 0s or more.
69103
- // Because a maximum of 6 digits are rendered, showing more than 3 leading 0s is not ideal
69104
- // because then at least half of the displayed digits will be leading 0s.
69105
- DefaultUnitFormat.exponentialLowerBound = 0.000995;
69106
- // Use exponential formatting for numbers whose magnitude cannot otherwise be displayed
69107
- // with 6 digits or less.
69108
- DefaultUnitFormat.exponentialUpperBound = 999999.5;
69109
69157
 
69110
69158
  /**
69111
- * Format for decimal numbers with units.
69159
+ * Mapping configuration corresponding to a text mapping
69112
69160
  */
69113
- class DecimalUnitFormat extends UnitFormat {
69114
- constructor(locale, { minimumFractionDigits = 0, maximumFractionDigits = Math.max(3, minimumFractionDigits), unitScale = passthroughUnitScale } = {
69115
- minimumFractionDigits: 0,
69116
- maximumFractionDigits: 3,
69117
- unitScale: passthroughUnitScale
69118
- }) {
69119
- super();
69120
- this.scaledUnitFormatters = new Map();
69121
- // Workaround to avoid a ts error about signDisplay not accepting the value 'negative'.
69122
- // It has been supported by browsers since 8/23, but TypeScript still hasn't
69123
- // added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269
69124
- const signDisplay = 'negative';
69125
- const intlNumberFormatOptions = {
69126
- maximumFractionDigits,
69127
- minimumFractionDigits,
69128
- signDisplay
69129
- };
69130
- for (const scaledUnit of unitScale.supportedScaledUnits) {
69131
- this.scaledUnitFormatters.set(scaledUnit.scaleFactor, scaledUnit.scaledUnitFormatFactory({
69132
- locale,
69133
- intlNumberFormatOptions
69134
- }));
69135
- }
69136
- this.unitScale = unitScale;
69137
- this.minimumFractionDigits = minimumFractionDigits;
69138
- this.maximumFractionDigits = maximumFractionDigits;
69139
- }
69140
- resolvedOptions() {
69141
- return {
69142
- unitScale: this.unitScale,
69143
- maximumFractionDigits: this.maximumFractionDigits,
69144
- minimumFractionDigits: this.minimumFractionDigits
69145
- };
69146
- }
69147
- tryFormat(number) {
69148
- const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number);
69149
- const scaledUnitFormatter = this.scaledUnitFormatters.get(scaledUnit.scaleFactor);
69150
- return scaledUnitFormatter.format(scaledValue);
69151
- }
69161
+ class MappingTextConfig extends MappingConfig {
69152
69162
  }
69153
69163
 
69154
69164
  /**
69155
- * Format for numbers (with optional units) in a number-text table column.
69165
+ * The group header view for the mapping column
69156
69166
  */
69157
- class NumberTextUnitFormat extends UnitFormat {
69158
- constructor(locale, options) {
69159
- super();
69160
- this._resolvedOptions = this.resolveOptions(options);
69161
- this.resolvedUnitFormat = this.resolveUnitFormat(locale, this._resolvedOptions);
69162
- }
69163
- resolvedOptions() {
69164
- return { ...this._resolvedOptions };
69165
- }
69166
- optionsMatch(targetOptions) {
69167
- const targetResolvedOptions = this.resolveOptions(targetOptions);
69168
- return (this._resolvedOptions.decimalDigits
69169
- === targetResolvedOptions.decimalDigits
69170
- && this._resolvedOptions.decimalMaximumDigits
69171
- === targetResolvedOptions.decimalMaximumDigits
69172
- && this._resolvedOptions.numberTextFormat
69173
- === targetResolvedOptions.numberTextFormat
69174
- && this._resolvedOptions.unitScale === targetResolvedOptions.unitScale);
69175
- }
69176
- tryFormat(number) {
69177
- return this.resolvedUnitFormat.format(number);
69167
+ class TableColumnMappingGroupHeaderView extends TableColumnTextGroupHeaderViewBase {
69168
+ constructor() {
69169
+ super(...arguments);
69170
+ this.textHidden = false;
69178
69171
  }
69179
- resolveUnitFormat(locale, options) {
69180
- const { numberTextFormat, decimalMaximumDigits, decimalDigits, unitScale } = options;
69181
- if (numberTextFormat === NumberTextFormat.default) {
69182
- return new DefaultUnitFormat(locale, {
69183
- unitScale
69184
- });
69185
- }
69186
- if (typeof decimalDigits === 'number') {
69187
- return new DecimalUnitFormat(locale, {
69188
- minimumFractionDigits: decimalDigits,
69189
- maximumFractionDigits: decimalDigits,
69190
- unitScale
69191
- });
69172
+ updateText() {
69173
+ this.resetState();
69174
+ if (!this.columnConfig) {
69175
+ return;
69192
69176
  }
69193
- return new DecimalUnitFormat(locale, {
69194
- minimumFractionDigits: 0,
69195
- maximumFractionDigits: decimalMaximumDigits,
69196
- unitScale
69197
- });
69198
- }
69199
- resolveOptions(options) {
69200
- if (!options || options.numberTextFormat === NumberTextFormat.default) {
69201
- return {
69202
- numberTextFormat: NumberTextFormat.default,
69203
- decimalDigits: undefined,
69204
- decimalMaximumDigits: undefined,
69205
- unitScale: options?.unitScale ?? passthroughUnitScale
69206
- };
69177
+ const value = this.groupHeaderValue;
69178
+ const mappingConfig = this.columnConfig.mappingConfigs.get(value);
69179
+ if (mappingConfig instanceof MappingIconConfig) {
69180
+ this.severity = mappingConfig.severity;
69181
+ this.text = mappingConfig.text ?? '';
69182
+ this.visualizationTemplate = mappingConfig.iconTemplate;
69207
69183
  }
69208
- const hasDecimalDigits = typeof options.decimalDigits === 'number';
69209
- const hasDecimalMaximumDigits = typeof options.decimalMaximumDigits === 'number';
69210
- if (hasDecimalDigits && hasDecimalMaximumDigits) {
69211
- throw new Error('decimalDigits and decimalMaximumDigits are mutually exclusive. Do not specify both.');
69184
+ else if (mappingConfig instanceof MappingSpinnerConfig) {
69185
+ this.text = mappingConfig.text ?? '';
69186
+ this.visualizationTemplate = mappingConfig.spinnerTemplate;
69212
69187
  }
69213
- if (!hasDecimalDigits && !hasDecimalMaximumDigits) {
69214
- return {
69215
- numberTextFormat: NumberTextFormat.decimal,
69216
- decimalDigits: NumberTextUnitFormat.defaultDecimalDigits,
69217
- decimalMaximumDigits: undefined,
69218
- unitScale: options.unitScale ?? passthroughUnitScale
69219
- };
69188
+ else if (mappingConfig instanceof MappingTextConfig) {
69189
+ this.text = mappingConfig.text ?? '';
69220
69190
  }
69221
- return {
69222
- numberTextFormat: NumberTextFormat.decimal,
69223
- decimalDigits: options.decimalDigits,
69224
- decimalMaximumDigits: options.decimalMaximumDigits,
69225
- unitScale: options.unitScale ?? passthroughUnitScale
69226
- };
69191
+ }
69192
+ resetState() {
69193
+ this.text = '';
69194
+ this.visualizationTemplate = undefined;
69195
+ this.severity = IconSeverity.default;
69227
69196
  }
69228
69197
  }
69229
- NumberTextUnitFormat.defaultDecimalDigits = 2;
69198
+ __decorate$1([
69199
+ observable
69200
+ ], TableColumnMappingGroupHeaderView.prototype, "severity", void 0);
69201
+ __decorate$1([
69202
+ observable
69203
+ ], TableColumnMappingGroupHeaderView.prototype, "visualizationTemplate", void 0);
69204
+ const mappingGroupHeaderView = TableColumnMappingGroupHeaderView.compose({
69205
+ baseName: 'table-column-mapping-group-header-view',
69206
+ template: template$8,
69207
+ styles: styles$9
69208
+ });
69209
+ DesignSystem.getOrCreate()
69210
+ .withPrefix('nimble')
69211
+ .register(mappingGroupHeaderView());
69212
+ const tableColumnMappingGroupHeaderViewTag = 'nimble-table-column-mapping-group-header-view';
69213
+
69214
+ const styles$8 = css `
69215
+ ${display('inline-flex')}
69216
+
69217
+ :host {
69218
+ gap: ${smallPadding};
69219
+ align-items: center;
69220
+ }
69221
+
69222
+ .reserve-icon-size {
69223
+ flex-shrink: 0;
69224
+ width: ${iconSize};
69225
+ height: ${iconSize};
69226
+ }
69227
+
69228
+ .text {
69229
+ flex-shrink: 1;
69230
+ font: ${bodyFont};
69231
+ color: ${bodyFontColor};
69232
+ white-space: nowrap;
69233
+ overflow: hidden;
69234
+ text-overflow: ellipsis;
69235
+ }
69236
+ `;
69237
+
69238
+ // prettier-ignore
69239
+ const template$7 = html `
69240
+ ${when(x => x.visualizationTemplate, html `
69241
+ <span class="reserve-icon-size">
69242
+ ${x => x.visualizationTemplate}
69243
+ </span>
69244
+ `)}
69245
+ ${when(x => !x.textHidden, html `
69246
+ <span
69247
+ ${overflow('hasOverflow')}
69248
+ title=${x => (x.hasOverflow && x.text ? x.text : null)}
69249
+ class="text"
69250
+ >
69251
+ ${x => x.text}
69252
+ </span>
69253
+ `)}
69254
+ `;
69230
69255
 
69231
- const numberTextValidityFlagNames = [
69232
- 'invalidDecimalDigits',
69233
- 'invalidDecimalMaximumDigits',
69234
- 'decimalDigitsMutuallyExclusiveWithDecimalMaximumDigits',
69235
- 'moreThanOneUnitSpecified'
69236
- ];
69237
- // The maximum and minimum allowed configuration for 'maximumFractionDigits'
69238
- // and 'minimumFractionDigits' on the NumberFormat.
69239
- const minimumValidDecimalDigits = 0;
69240
- const maximumValidDecimalDigits = 20;
69241
69256
  /**
69242
- * Validator for TableColumnNumberText.
69257
+ * The cell view for the mapping column
69243
69258
  */
69244
- class TableColumnNumberTextValidator extends ColumnValidator {
69259
+ class TableColumnMappingCellView extends TableCellView {
69245
69260
  constructor() {
69246
- super(numberTextValidityFlagNames);
69247
- }
69248
- validateDecimalDigits(format, decimalDigits) {
69249
- const shouldValidateDecimalDigitsValue = format === NumberTextFormat.decimal
69250
- && typeof decimalDigits === 'number';
69251
- const invalid = shouldValidateDecimalDigitsValue
69252
- ? this.isInvalidDecimalDigitsValue(decimalDigits)
69253
- : false;
69254
- this.setConditionValue('invalidDecimalDigits', invalid);
69261
+ super(...arguments);
69262
+ this.textHidden = false;
69263
+ /** @internal */
69264
+ this.hasOverflow = false;
69255
69265
  }
69256
- validateDecimalMaximumDigits(format, decimalMaximumDigits) {
69257
- const shouldValidateDecimalDigitsValue = format === NumberTextFormat.decimal
69258
- && typeof decimalMaximumDigits === 'number';
69259
- const invalid = shouldValidateDecimalDigitsValue
69260
- ? this.isInvalidDecimalDigitsValue(decimalMaximumDigits)
69261
- : false;
69262
- this.setConditionValue('invalidDecimalMaximumDigits', invalid);
69266
+ columnConfigChanged() {
69267
+ this.updateState();
69263
69268
  }
69264
- validateNoMutuallyExclusiveProperties(format, decimalDigits, decimalMaximumDigits) {
69265
- const shouldValidateMutuallyExclusiveProperties = format === NumberTextFormat.decimal;
69266
- const invalid = shouldValidateMutuallyExclusiveProperties
69267
- ? typeof decimalDigits === 'number'
69268
- && typeof decimalMaximumDigits === 'number'
69269
- : false;
69270
- this.setConditionValue('decimalDigitsMutuallyExclusiveWithDecimalMaximumDigits', invalid);
69269
+ cellRecordChanged() {
69270
+ this.updateState();
69271
69271
  }
69272
- validateAtMostOneUnit(unitElements) {
69273
- const invalid = unitElements.length > 1;
69274
- this.setConditionValue('moreThanOneUnitSpecified', invalid);
69272
+ updateState() {
69273
+ this.resetState();
69274
+ if (!this.columnConfig || !this.cellRecord) {
69275
+ return;
69276
+ }
69277
+ const value = this.cellRecord.value;
69278
+ if (value === undefined || value === null) {
69279
+ return;
69280
+ }
69281
+ const mappingConfig = this.columnConfig.mappingConfigs.get(value);
69282
+ if (mappingConfig instanceof MappingIconConfig) {
69283
+ this.severity = mappingConfig.severity;
69284
+ this.text = mappingConfig.text;
69285
+ this.visualizationTemplate = mappingConfig.iconTemplate;
69286
+ this.textHidden = mappingConfig.textHidden;
69287
+ }
69288
+ else if (mappingConfig instanceof MappingSpinnerConfig) {
69289
+ this.text = mappingConfig.text;
69290
+ this.visualizationTemplate = mappingConfig.spinnerTemplate;
69291
+ this.textHidden = mappingConfig.textHidden;
69292
+ }
69293
+ else if (mappingConfig instanceof MappingTextConfig) {
69294
+ this.text = mappingConfig.text;
69295
+ this.textHidden = false;
69296
+ }
69275
69297
  }
69276
- isInvalidDecimalDigitsValue(decimalDigits) {
69277
- return (decimalDigits < minimumValidDecimalDigits
69278
- || decimalDigits > maximumValidDecimalDigits);
69298
+ resetState() {
69299
+ this.text = undefined;
69300
+ this.textHidden = false;
69301
+ this.visualizationTemplate = undefined;
69302
+ this.severity = IconSeverity.default;
69279
69303
  }
69280
69304
  }
69305
+ __decorate$1([
69306
+ observable
69307
+ ], TableColumnMappingCellView.prototype, "severity", void 0);
69308
+ __decorate$1([
69309
+ observable
69310
+ ], TableColumnMappingCellView.prototype, "text", void 0);
69311
+ __decorate$1([
69312
+ observable
69313
+ ], TableColumnMappingCellView.prototype, "visualizationTemplate", void 0);
69314
+ __decorate$1([
69315
+ observable
69316
+ ], TableColumnMappingCellView.prototype, "textHidden", void 0);
69317
+ __decorate$1([
69318
+ observable
69319
+ ], TableColumnMappingCellView.prototype, "hasOverflow", void 0);
69320
+ const mappingCellView = TableColumnMappingCellView.compose({
69321
+ baseName: 'table-column-mapping-cell-view',
69322
+ template: template$7,
69323
+ styles: styles$8
69324
+ });
69325
+ DesignSystem.getOrCreate().withPrefix('nimble').register(mappingCellView());
69326
+ const tableColumnMappingCellViewTag = 'nimble-table-column-mapping-cell-view';
69281
69327
 
69282
69328
  /**
69283
- * The base class for an element that provides a unit scale for scaled unit conversions
69329
+ * Width mode for the mapping column
69284
69330
  */
69285
- class Unit extends FoundationElement {
69286
- }
69287
- __decorate$1([
69288
- observable
69289
- ], Unit.prototype, "resolvedUnitScale", void 0);
69331
+ const TableColumnMappingWidthMode = {
69332
+ default: undefined,
69333
+ iconSize: 'icon-size'
69334
+ };
69290
69335
 
69291
69336
  /**
69292
- * The table column for displaying numbers as text.
69337
+ * Table column that maps number, boolean, or string values to an icon, a spinner,
69338
+ * text, or an icon/spinner with text.
69293
69339
  */
69294
- class TableColumnNumberText extends mixinTextBase((TableColumnTextBase)) {
69295
- constructor() {
69296
- super(...arguments);
69297
- this.langSubscriber = {
69298
- handleChange: () => {
69299
- this.updateColumnConfig();
69300
- }
69301
- };
69302
- this.unitSubscriber = {
69303
- handleChange: () => {
69304
- this.updateColumnConfig();
69305
- }
69306
- };
69307
- }
69308
- connectedCallback() {
69309
- super.connectedCallback();
69310
- lang.subscribe(this.langSubscriber, this);
69311
- this.updateColumnConfig();
69312
- }
69313
- disconnectedCallback() {
69314
- super.disconnectedCallback();
69315
- lang.unsubscribe(this.langSubscriber, this);
69316
- }
69317
- placeholderChanged() {
69318
- this.updateColumnConfig();
69340
+ class TableColumnMapping extends mixinGroupableColumnAPI(mixinFractionalWidthColumnAPI((TableColumnEnumBase))) {
69341
+ minPixelWidthChanged() {
69342
+ if (this.widthMode !== TableColumnMappingWidthMode.iconSize) {
69343
+ this.columnInternals.minPixelWidth = this.getConfiguredMinPixelWidth();
69344
+ }
69319
69345
  }
69320
69346
  getColumnInternalsOptions() {
69321
69347
  return {
69322
69348
  cellRecordFieldNames: ['value'],
69323
- cellViewTag: tableColumnNumberTextCellViewTag,
69324
- groupHeaderViewTag: tableColumnNumberTextGroupHeaderTag,
69349
+ cellViewTag: tableColumnMappingCellViewTag,
69350
+ groupHeaderViewTag: tableColumnMappingGroupHeaderViewTag,
69325
69351
  delegatedEvents: [],
69326
69352
  sortOperation: TableColumnSortOperation.basic,
69327
- validator: new TableColumnNumberTextValidator()
69353
+ validator: new TableColumnMappingValidator()
69328
69354
  };
69329
69355
  }
69330
- updateUnitNotifier() {
69331
- if (this.unitNotifier) {
69332
- this.unitNotifier.unsubscribe(this.unitSubscriber);
69333
- this.unitNotifier = undefined;
69356
+ createColumnConfig(mappingConfigs) {
69357
+ return {
69358
+ mappingConfigs
69359
+ };
69360
+ }
69361
+ createMappingConfig(mapping) {
69362
+ if (mapping instanceof MappingIcon) {
69363
+ return new MappingIconConfig(mapping.resolvedIcon, mapping.severity, mapping.text, mapping.textHidden);
69334
69364
  }
69335
- if (this.unit) {
69336
- const notifier = Observable.getNotifier(this.unit);
69337
- notifier.subscribe(this.unitSubscriber, 'resolvedUnitScale');
69338
- this.unitNotifier = notifier;
69365
+ if (mapping instanceof MappingSpinner) {
69366
+ return new MappingSpinnerConfig(mapping.text, mapping.textHidden);
69339
69367
  }
69340
- }
69341
- formatChanged() {
69342
- this.updateColumnConfig();
69343
- }
69344
- alignmentChanged() {
69345
- this.updateColumnConfig();
69346
- }
69347
- decimalDigitsChanged() {
69348
- this.updateColumnConfig();
69349
- }
69350
- decimalMaximumDigitsChanged() {
69351
- this.updateColumnConfig();
69352
- }
69353
- unitElementsChanged() {
69354
- void this.updateUnit();
69355
- }
69356
- async updateUnit() {
69357
- this.unit = undefined;
69358
- if (this.unitElements) {
69359
- await waitUntilCustomElementsDefinedAsync(this.unitElements);
69360
- this.unit = this.unitElements.find((x) => x instanceof Unit);
69368
+ if (mapping instanceof MappingText) {
69369
+ return new MappingTextConfig(mapping.text);
69361
69370
  }
69362
- this.updateUnitNotifier();
69363
- }
69364
- unitChanged() {
69365
- this.updateColumnConfig();
69371
+ // Getting here would indicate a programming error, b/c validation will prevent
69372
+ // this function from running when there is an unsupported mapping.
69373
+ throw new Error('Unsupported mapping');
69366
69374
  }
69367
- updateColumnConfig() {
69368
- const validator = this.columnInternals.validator;
69369
- validator.validateDecimalDigits(this.format, this.decimalDigits);
69370
- validator.validateDecimalMaximumDigits(this.format, this.decimalMaximumDigits);
69371
- validator.validateNoMutuallyExclusiveProperties(this.format, this.decimalDigits, this.decimalMaximumDigits);
69372
- validator.validateAtMostOneUnit(this.unitElements ?? []);
69373
- if (validator.isValid()) {
69374
- const columnConfig = {
69375
- formatter: this.createFormatter(),
69376
- alignment: this.determineCellContentAlignment(),
69377
- placeholder: this.placeholder
69378
- };
69379
- this.columnInternals.columnConfig = columnConfig;
69375
+ widthModeChanged() {
69376
+ if (this.widthMode === TableColumnMappingWidthMode.iconSize) {
69377
+ this.columnInternals.resizingDisabled = true;
69378
+ this.columnInternals.hideHeaderIndicators = true;
69379
+ this.columnInternals.pixelWidth = singleIconColumnWidth;
69380
+ this.columnInternals.minPixelWidth = singleIconColumnWidth;
69380
69381
  }
69381
69382
  else {
69382
- this.columnInternals.columnConfig = undefined;
69383
+ this.columnInternals.resizingDisabled = false;
69384
+ this.columnInternals.hideHeaderIndicators = false;
69385
+ this.columnInternals.pixelWidth = undefined;
69386
+ this.columnInternals.minPixelWidth = this.getConfiguredMinPixelWidth();
69383
69387
  }
69384
69388
  }
69385
- createFormatter() {
69386
- const unitScale = this.unit?.resolvedUnitScale;
69387
- return new NumberTextUnitFormat(lang.getValueFor(this), {
69388
- // Attribute values sometimes resolve to either null or undefined
69389
- // See https://github.com/microsoft/fast/issues/6630
69390
- numberTextFormat: this.format ?? undefined,
69391
- decimalDigits: this.decimalDigits ?? undefined,
69392
- decimalMaximumDigits: this.decimalMaximumDigits ?? undefined,
69393
- unitScale
69394
- });
69395
- }
69396
- determineCellContentAlignment() {
69397
- if (this.alignment === NumberTextAlignment.left) {
69398
- return TextCellViewBaseAlignment.left;
69399
- }
69400
- if (this.alignment === NumberTextAlignment.right) {
69401
- return TextCellViewBaseAlignment.right;
69402
- }
69403
- // Look at format and decimal max digits and unit to determine the default alignment
69404
- if (this.format === NumberTextFormat.decimal
69405
- && typeof this.decimalMaximumDigits !== 'number'
69406
- && !this.unit) {
69407
- return TextCellViewBaseAlignment.right;
69389
+ getConfiguredMinPixelWidth() {
69390
+ if (typeof this.minPixelWidth === 'number') {
69391
+ return this.minPixelWidth;
69408
69392
  }
69409
- return TextCellViewBaseAlignment.left;
69393
+ return defaultMinPixelWidth;
69410
69394
  }
69411
69395
  }
69412
69396
  __decorate$1([
69413
- attr
69414
- ], TableColumnNumberText.prototype, "format", void 0);
69415
- __decorate$1([
69416
- attr
69417
- ], TableColumnNumberText.prototype, "alignment", void 0);
69418
- __decorate$1([
69419
- attr({ attribute: 'decimal-digits', converter: nullableNumberConverter })
69420
- ], TableColumnNumberText.prototype, "decimalDigits", void 0);
69421
- __decorate$1([
69422
- attr({
69423
- attribute: 'decimal-maximum-digits',
69424
- converter: nullableNumberConverter
69425
- })
69426
- ], TableColumnNumberText.prototype, "decimalMaximumDigits", void 0);
69427
- __decorate$1([
69428
- observable
69429
- ], TableColumnNumberText.prototype, "unitElements", void 0);
69430
- __decorate$1([
69431
- observable
69432
- ], TableColumnNumberText.prototype, "unit", void 0);
69433
- const nimbleTableColumnNumberText = TableColumnNumberText.compose({
69434
- baseName: 'table-column-number-text',
69397
+ attr({ attribute: 'width-mode' })
69398
+ ], TableColumnMapping.prototype, "widthMode", void 0);
69399
+ const nimbleTableColumnMapping = TableColumnMapping.compose({
69400
+ baseName: 'table-column-mapping',
69435
69401
  template: template$9,
69436
- styles: styles$e
69402
+ styles: styles$a
69437
69403
  });
69438
69404
  DesignSystem.getOrCreate()
69439
69405
  .withPrefix('nimble')
69440
- .register(nimbleTableColumnNumberText());
69406
+ .register(nimbleTableColumnMapping());
69407
+
69408
+ // Avoiding a wrapping <template> and be careful about starting and ending whitespace
69409
+ // so the template can be composed into other column header templates
69410
+ // prettier-ignore
69411
+ const template$6 = html `<span
69412
+ ${overflow('hasOverflow')}
69413
+ class="header-content"
69414
+ title=${x => (x.hasOverflow && x.headerTextContent ? x.headerTextContent : null)}
69415
+ >
69416
+ <slot ${ref('contentSlot')}></slot>
69417
+ <slot ${slotted({ property: 'unitElements', filter: elements() })} name="unit"></slot>
69418
+ </span>`;
69441
69419
 
69442
69420
  /**
69443
- * Converts a Mapping key (which is a string when configured in HTML) to the
69444
- * given keyType. The converted value can then be used to compare against
69445
- * values in the table data.
69421
+ * The group header view for displaying number fields as text.
69446
69422
  */
69447
- const resolveKeyWithType = (key, keyType) => {
69448
- if (keyType === 'number') {
69449
- const converted = nullableNumberConverter.fromView(key);
69450
- return converted === null ? undefined : converted;
69423
+ class TableColumnNumberTextGroupHeaderView extends TableColumnTextGroupHeaderViewBase {
69424
+ updateText() {
69425
+ this.text = this.columnConfig?.formatter?.format(this.groupHeaderValue) ?? '';
69451
69426
  }
69452
- if (keyType === 'boolean') {
69453
- if (key === false || key === 'false') {
69454
- return false;
69455
- }
69456
- if (key === true || key === 'true') {
69457
- return true;
69458
- }
69459
- return undefined;
69427
+ }
69428
+ const tableColumnNumberTextGroupHeaderView = TableColumnNumberTextGroupHeaderView.compose({
69429
+ baseName: 'table-column-number-text-group-header-view',
69430
+ template: template$b,
69431
+ styles: styles$c
69432
+ });
69433
+ DesignSystem.getOrCreate()
69434
+ .withPrefix('nimble')
69435
+ .register(tableColumnNumberTextGroupHeaderView());
69436
+ const tableColumnNumberTextGroupHeaderTag = 'nimble-table-column-number-text-group-header-view';
69437
+
69438
+ /**
69439
+ * A cell view for displaying number fields as text
69440
+ */
69441
+ class TableColumnNumberTextCellView extends TableColumnTextCellViewBase {
69442
+ columnConfigChanged() {
69443
+ super.columnConfigChanged();
69444
+ this.alignment = this.columnConfig?.alignment ?? TextCellViewBaseAlignment.left;
69460
69445
  }
69461
- return key?.toString() ?? undefined;
69446
+ updateText() {
69447
+ this.text = this.columnConfig?.formatter?.format(this.cellRecord?.value) ?? '';
69448
+ }
69449
+ }
69450
+ const numberTextCellView = TableColumnNumberTextCellView.compose({
69451
+ baseName: 'table-column-number-text-cell-view',
69452
+ template: template$a,
69453
+ styles: styles$b
69454
+ });
69455
+ DesignSystem.getOrCreate().withPrefix('nimble').register(numberTextCellView());
69456
+ const tableColumnNumberTextCellViewTag = 'nimble-table-column-number-text-cell-view';
69457
+
69458
+ /**
69459
+ * Formatting scheme for the number-text table column
69460
+ */
69461
+ const NumberTextFormat = {
69462
+ default: undefined,
69463
+ decimal: 'decimal'
69464
+ };
69465
+ /**
69466
+ * The aligment of the value in the number-text table column.
69467
+ * The `default` alignment is determined by the column's `NumberTextFormat`.
69468
+ */
69469
+ const NumberTextAlignment = {
69470
+ default: undefined,
69471
+ left: 'left',
69472
+ right: 'right'
69462
69473
  };
69463
69474
 
69464
69475
  /**
69465
- * Base class for table columns that map values to content
69476
+ * The base class for unit formats.
69466
69477
  */
69467
- class TableColumnEnumBase extends TableColumn {
69468
- constructor() {
69469
- super(...arguments);
69470
- /** @internal */
69471
- this.mappingNotifiers = [];
69472
- /** @internal */
69473
- this.mappings = [];
69474
- this.keyType = 'string';
69475
- }
69478
+ class UnitFormat {
69476
69479
  /**
69477
- * @internal
69478
- *
69479
- * Triggers a request to update the columnConfig when any observable property on
69480
- * a mapping is updated.
69480
+ * Formats a number value to a string.
69481
+ * For nullish values or values that result in an exception being thrown, empty string is returned
69481
69482
  */
69482
- handleChange(source, args) {
69483
- if (source instanceof Mapping$1 && typeof args === 'string') {
69484
- this.updateColumnConfig();
69485
- }
69486
- }
69487
- /**
69488
- * Called when any Mapping related state has changed.
69489
- */
69490
- updateColumnConfig() {
69491
- this.columnInternals.validator.validate(this.mappings, this.keyType);
69492
- this.columnInternals.columnConfig = this.checkValidity()
69493
- ? this.createColumnConfig(this.getMappingConfigs())
69494
- : undefined;
69495
- }
69496
- getMappingConfigs() {
69497
- const mappingConfigs = new Map();
69498
- this.mappings.forEach(mapping => {
69499
- const key = resolveKeyWithType(mapping.key, this.keyType);
69500
- if (key === undefined) {
69501
- throw Error('Key was invalid for type. Validation should have prevented this.');
69502
- }
69503
- const mappingConfig = this.createMappingConfig(mapping);
69504
- mappingConfigs.set(key, mappingConfig);
69505
- });
69506
- return mappingConfigs;
69507
- }
69508
- fieldNameChanged() {
69509
- this.columnInternals.dataRecordFieldNames = [this.fieldName];
69510
- this.columnInternals.operandDataRecordFieldName = this.fieldName;
69511
- }
69512
- mappingsChanged() {
69513
- this.updateColumnConfig();
69514
- this.observeMappings();
69515
- }
69516
- keyTypeChanged() {
69517
- this.updateColumnConfig();
69518
- }
69519
- removeMappingObservers() {
69520
- this.mappingNotifiers.forEach(notifier => {
69521
- notifier.unsubscribe(this);
69522
- });
69523
- this.mappingNotifiers = [];
69524
- }
69525
- observeMappings() {
69526
- this.removeMappingObservers();
69527
- for (const mapping of this.mappings) {
69528
- const notifier = Observable.getNotifier(mapping);
69529
- notifier.subscribe(this);
69530
- this.mappingNotifiers.push(notifier);
69483
+ format(value) {
69484
+ if (typeof value !== 'number') {
69485
+ return '';
69486
+ }
69487
+ try {
69488
+ return this.tryFormat(value);
69489
+ }
69490
+ catch {
69491
+ return '';
69531
69492
  }
69532
69493
  }
69533
69494
  }
69534
- __decorate$1([
69535
- observable
69536
- ], TableColumnEnumBase.prototype, "mappings", void 0);
69537
- __decorate$1([
69538
- attr({ attribute: 'field-name' })
69539
- ], TableColumnEnumBase.prototype, "fieldName", void 0);
69540
- __decorate$1([
69541
- attr({ attribute: 'key-type' })
69542
- ], TableColumnEnumBase.prototype, "keyType", void 0);
69543
69495
 
69544
- const styles$a = css `
69545
- ${styles$e}
69546
-
69547
- slot[name='mapping'] {
69548
- display: none;
69549
- }
69550
- `;
69551
-
69552
- const template$8 = html `${template$d}<slot ${slotted('mappings')} name="mapping"></slot>`;
69553
-
69554
- const enumBaseValidityFlagNames = [
69555
- 'invalidMappingKeyValueForType',
69556
- 'duplicateMappingKey',
69557
- 'missingKeyValue'
69558
- ];
69559
69496
  /**
69560
- * Validator base class for table columns containing mappings. Implementations MUST include enumBaseValidityFlagNames in validity flag names set.
69497
+ * A class that knows how to format a numeric value as a string that includes units.
69561
69498
  */
69562
- class TableColumnEnumBaseValidator extends ColumnValidator {
69563
- constructor(configValidityKeys) {
69564
- super(configValidityKeys);
69565
- }
69566
- validate(mappings, keyType) {
69567
- this.untrackAll();
69568
- const keys = mappings.map(mapping => mapping.key);
69569
- this.validateKeyValuesForType(keys, keyType);
69570
- this.validateUniqueKeys(keys, keyType);
69571
- this.validateNoMissingKeys(mappings);
69572
- }
69573
- validateKeyValuesForType(keys, keyType) {
69574
- // Ignore undefined keys, because validateNoMissingKeys covers that case.
69575
- // We should only set 'invalidMappingKeyValueForType' when there is a key,
69576
- // but it isn't appropriate for the type.
69577
- const invalid = keys.some(key => key !== undefined
69578
- && resolveKeyWithType(key, keyType) === undefined);
69579
- this.setConditionValue('invalidMappingKeyValueForType', invalid);
69580
- }
69581
- validateUniqueKeys(keys, keyType) {
69582
- const typedKeys = keys.map(x => resolveKeyWithType(x, keyType));
69583
- const invalid = new Set(typedKeys).size !== typedKeys.length;
69584
- this.setConditionValue('duplicateMappingKey', invalid);
69585
- }
69586
- validateNoMissingKeys(mappings) {
69587
- const invalid = mappings.some(mapping => mapping.key === undefined);
69588
- this.setConditionValue('missingKeyValue', invalid);
69499
+ class ScaledUnitFormat {
69500
+ constructor(scaledUnitFormatFactoryOptions) {
69501
+ this.locale = scaledUnitFormatFactoryOptions.locale;
69502
+ this.intlNumberFormatOptions = scaledUnitFormatFactoryOptions.intlNumberFormatOptions;
69589
69503
  }
69590
69504
  }
69591
69505
 
69592
- const iconValidityFlagNames = [
69593
- ...enumBaseValidityFlagNames,
69594
- 'unsupportedMappingType',
69595
- 'invalidIconName',
69596
- 'missingTextValue'
69597
- ];
69598
69506
  /**
69599
- * Validator for TableColumnIcon
69507
+ * A formatter for units that can be formatted/translated by Intl.NumberFormat
69600
69508
  */
69601
- class TableColumnIconValidator extends TableColumnEnumBaseValidator {
69602
- constructor() {
69603
- super(iconValidityFlagNames);
69604
- }
69605
- static isIconMappingElement(mapping) {
69606
- return mapping instanceof MappingIcon;
69607
- }
69608
- static isSupportedMappingElement(mapping) {
69609
- return (mapping instanceof MappingIcon
69610
- || mapping instanceof MappingSpinner
69611
- || mapping instanceof MappingText);
69612
- }
69613
- static hasUnresolvedIcon(mappingIcon) {
69614
- return (typeof mappingIcon.icon === 'string'
69615
- && mappingIcon.resolvedIcon === undefined);
69616
- }
69617
- validate(mappings, keyType) {
69618
- super.validate(mappings, keyType);
69619
- this.validateMappingTypes(mappings);
69620
- this.validateIconNames(mappings);
69621
- this.validateNoMissingText(mappings);
69622
- }
69623
- validateIconNames(mappings) {
69624
- const invalid = mappings
69625
- .filter(TableColumnIconValidator.isIconMappingElement)
69626
- .some(TableColumnIconValidator.hasUnresolvedIcon);
69627
- this.setConditionValue('invalidIconName', invalid);
69509
+ class IntlNumberFormatScaledUnitFormat extends ScaledUnitFormat {
69510
+ constructor(scaledUnitFormatFactoryOptions, unitSpecificIntlNumberFormatOptions) {
69511
+ super(scaledUnitFormatFactoryOptions);
69512
+ this.formatter = new Intl.NumberFormat(this.locale, {
69513
+ ...unitSpecificIntlNumberFormatOptions,
69514
+ // Application configured options override unit specific options
69515
+ ...this.intlNumberFormatOptions
69516
+ });
69628
69517
  }
69629
- validateNoMissingText(mappings) {
69630
- const invalid = mappings
69631
- .filter(TableColumnIconValidator.isSupportedMappingElement)
69632
- .some(mapping => mapping.text === undefined);
69633
- this.setConditionValue('missingTextValue', invalid);
69518
+ static createFactory(unitSpecificIntlNumberFormatOptions) {
69519
+ return (scaledUnitFormatFactoryOptions) => new IntlNumberFormatScaledUnitFormat(scaledUnitFormatFactoryOptions, unitSpecificIntlNumberFormatOptions);
69634
69520
  }
69635
- validateMappingTypes(mappings) {
69636
- const valid = mappings.every(TableColumnIconValidator.isSupportedMappingElement);
69637
- this.setConditionValue('unsupportedMappingType', !valid);
69521
+ format(value) {
69522
+ return this.formatter.format(value);
69638
69523
  }
69639
69524
  }
69640
69525
 
69641
- const styles$9 = css `
69642
- ${display('inline-flex')}
69643
-
69644
- :host {
69645
- gap: ${smallPadding};
69646
- align-items: center;
69647
- }
69648
-
69649
- .reserve-icon-size {
69650
- flex-shrink: 0;
69651
- width: ${iconSize};
69652
- height: ${iconSize};
69653
- }
69654
-
69655
- .text {
69656
- flex-shrink: 1;
69657
- font: ${bodyFont};
69658
- color: ${bodyFontColor};
69659
- white-space: nowrap;
69660
- overflow: hidden;
69661
- text-overflow: ellipsis;
69662
- }
69663
- `;
69664
-
69665
- // prettier-ignore
69666
- const template$7 = html `
69667
- ${when(x => x.visualizationTemplate, html `
69668
- <span class="reserve-icon-size">
69669
- ${x => x.visualizationTemplate}
69670
- </span>
69671
- `)}
69672
- <span
69673
- ${overflow('hasOverflow')}
69674
- title="${x => (x.hasOverflow && x.text ? x.text : null)}"
69675
- class="text"
69676
- >${x => x.text}</span>
69677
- `;
69678
-
69679
69526
  /**
69680
- * Predefined icon appearance states
69681
- * @public
69527
+ * A unit that represents a scaled version of a base unit.
69682
69528
  */
69683
- const IconSeverity = {
69684
- default: undefined,
69685
- error: 'error',
69686
- warning: 'warning',
69687
- success: 'success',
69688
- information: 'information'
69689
- };
69529
+ class ScaledUnit {
69530
+ constructor(scaleFactor, scaledUnitFormatFactory) {
69531
+ this.scaleFactor = scaleFactor;
69532
+ this.scaledUnitFormatFactory = scaledUnitFormatFactory;
69533
+ }
69534
+ isBase() {
69535
+ return this.scaleFactor === 1;
69536
+ }
69537
+ }
69690
69538
 
69691
69539
  /**
69692
- * Common state shared across Mapping Config
69540
+ * A unit scale consisting of a set of scaled units.
69693
69541
  */
69694
- class MappingConfig {
69695
- constructor(text) {
69696
- this.text = text;
69542
+ class UnitScale {
69543
+ constructor(supportedScaledUnits) {
69544
+ this.supportedScaledUnits = supportedScaledUnits;
69545
+ const unitsSorted = supportedScaledUnits.every((curr, i, arr) => i === 0 || arr[i - 1].scaleFactor < curr.scaleFactor);
69546
+ if (!unitsSorted) {
69547
+ throw new Error('Supported scaled units must have unique and ordered scale factors');
69548
+ }
69549
+ const baseScaledUnit = supportedScaledUnits.find(x => x.isBase());
69550
+ if (!baseScaledUnit) {
69551
+ throw new Error('Supported scaled units must include a base scaled unit (scale factor=1)');
69552
+ }
69553
+ this.supportedScaledUnits = supportedScaledUnits;
69554
+ this.baseScaledUnit = baseScaledUnit;
69555
+ }
69556
+ // Note that for the sake of reducing complexity in the implementation,
69557
+ // we do NOT consider the effects of rounding when picking the unit to
69558
+ // use for a given value. If formatting results in rounding, a value
69559
+ // may be shown with an unexpected unit. Examples:
69560
+ // - 999 bytes with two significant digits => "1000 bytes" (instead of "1 kB")
69561
+ // - 0.00000000000000001 volts (= 0.01 fV) with one fractional digit => "0 fV" (instead of "0 volts")
69562
+ scaleNumber(number) {
69563
+ const magnitude = Math.abs(number);
69564
+ const onlyBaseScaledUnit = this.supportedScaledUnits.length === 1;
69565
+ if (onlyBaseScaledUnit
69566
+ || magnitude === 0
69567
+ || magnitude === Infinity
69568
+ || Number.isNaN(magnitude)) {
69569
+ return { scaledValue: number, scaledUnit: this.baseScaledUnit };
69570
+ }
69571
+ for (let i = this.supportedScaledUnits.length - 1; i >= 0; i -= 1) {
69572
+ const scaledUnit = this.supportedScaledUnits[i];
69573
+ if (magnitude / scaledUnit.scaleFactor >= 1) {
69574
+ return {
69575
+ scaledValue: number / scaledUnit.scaleFactor,
69576
+ scaledUnit
69577
+ };
69578
+ }
69579
+ }
69580
+ const smallestUnit = this.supportedScaledUnits[0];
69581
+ return {
69582
+ scaledValue: number / smallestUnit.scaleFactor,
69583
+ scaledUnit: smallestUnit
69584
+ };
69697
69585
  }
69698
69586
  }
69699
69587
 
69700
- // Create an empty template containing only a space because creating a ViewTemplate
69701
- // with an empty string throws an exception at runtime.
69702
- // prettier-ignore
69703
- const emptyTemplate = html ` `;
69704
- const createIconTemplate = (icon) => {
69705
- if (icon === undefined) {
69706
- return emptyTemplate;
69707
- }
69708
- return html `
69709
- <${icon}
69710
- title="${x => (x.textHidden ? x.text : '')}"
69711
- role="img"
69712
- aria-label="${x => x.text}"
69713
- aria-hidden="${x => (x.textHidden ? 'false' : 'true')}"
69714
- severity="${x => x.severity}"
69715
- >
69716
- </${icon}>
69717
- `;
69718
- };
69719
69588
  /**
69720
- * Mapping configuration corresponding to a icon mapping
69589
+ * Unit scale that is used to passthrough a number without applying scaling or units
69721
69590
  */
69722
- class MappingIconConfig extends MappingConfig {
69723
- constructor(resolvedIcon, severity, text, textHidden) {
69724
- super(text);
69725
- this.severity = severity;
69726
- this.textHidden = textHidden;
69727
- this.iconTemplate = createIconTemplate(resolvedIcon);
69591
+ class PassthroughUnitScale extends UnitScale {
69592
+ constructor() {
69593
+ super([
69594
+ new ScaledUnit(10 ** 0, IntlNumberFormatScaledUnitFormat.createFactory({}))
69595
+ ]);
69728
69596
  }
69729
69597
  }
69598
+ const passthroughUnitScale = new PassthroughUnitScale();
69730
69599
 
69731
- const spinnerTemplate = html `
69732
- <${spinnerTag}
69733
- title="${x => (x.textHidden ? x.text : '')}"
69734
- aria-label="${x => x.text}"
69735
- aria-hidden="${x => (x.textHidden ? 'false' : 'true')}"
69736
- >
69737
- </${spinnerTag}>
69738
- `;
69600
+ // Workaround to avoid ts errors about signDisplay not accepting the value 'negative'.
69601
+ // It has been supported by browsers since 8/23, but TypeScript still hasn't
69602
+ // added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269
69603
+ const signDisplay = 'negative';
69739
69604
  /**
69740
- * Mapping configuration corresponding to a spinner mapping
69605
+ * Format for numbers with units to show in a tabular form.
69606
+ * Large and tiny numbers are shown exponentially and the rest as decimal.
69741
69607
  */
69742
- class MappingSpinnerConfig extends MappingConfig {
69743
- constructor(text, textHidden) {
69744
- super(text);
69745
- this.textHidden = textHidden;
69746
- this.spinnerTemplate = spinnerTemplate;
69608
+ class DefaultUnitFormat extends UnitFormat {
69609
+ constructor(locale, { unitScale = passthroughUnitScale } = {
69610
+ unitScale: passthroughUnitScale
69611
+ }) {
69612
+ super();
69613
+ // Format options to use by default. It renders the number with a maximum of 6 signficant digits (including zero before decimal point).
69614
+ this.defaultIntlNumberFormatOptions = {
69615
+ maximumSignificantDigits: DefaultUnitFormat.maximumDigits,
69616
+ maximumFractionDigits: DefaultUnitFormat.maximumDigits - 1,
69617
+ roundingPriority: 'lessPrecision',
69618
+ signDisplay
69619
+ };
69620
+ this.defaultScaledUnitFormatters = new Map();
69621
+ // Format options for numbers that should be displayed in exponential notation. This should be used
69622
+ // for numbers with magintudes over 'exponentialUpperBound' or under 'exponentialLowerBound'.
69623
+ this.exponentialIntlNumberFormatOptions = {
69624
+ maximumSignificantDigits: DefaultUnitFormat.maximumDigits,
69625
+ notation: 'scientific',
69626
+ signDisplay
69627
+ };
69628
+ for (const unit of unitScale.supportedScaledUnits) {
69629
+ this.defaultScaledUnitFormatters.set(unit.scaleFactor, unit.scaledUnitFormatFactory({
69630
+ locale,
69631
+ intlNumberFormatOptions: this.defaultIntlNumberFormatOptions
69632
+ }));
69633
+ }
69634
+ this.exponentialScaledUnitFormatter = unitScale.baseScaledUnit.scaledUnitFormatFactory({
69635
+ locale,
69636
+ intlNumberFormatOptions: this.exponentialIntlNumberFormatOptions
69637
+ });
69638
+ this.unitScale = unitScale;
69639
+ }
69640
+ resolvedOptions() {
69641
+ return {
69642
+ unitScale: this.unitScale
69643
+ };
69644
+ }
69645
+ tryFormat(number) {
69646
+ const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number);
69647
+ const absoluteValue = Math.abs(scaledValue);
69648
+ const useExponential = absoluteValue !== 0
69649
+ && (absoluteValue >= DefaultUnitFormat.exponentialUpperBound
69650
+ || absoluteValue < DefaultUnitFormat.exponentialLowerBound);
69651
+ return useExponential
69652
+ ? this.exponentialScaledUnitFormatter.format(number)
69653
+ : this.defaultScaledUnitFormatters
69654
+ .get(scaledUnit.scaleFactor)
69655
+ .format(scaledValue);
69747
69656
  }
69748
69657
  }
69658
+ // The maximum number of digits that should be rendered for any given value.
69659
+ DefaultUnitFormat.maximumDigits = 6;
69660
+ // Use exponential notation for numbers that will be rendered with 3 leading 0s or more.
69661
+ // Because a maximum of 6 digits are rendered, showing more than 3 leading 0s is not ideal
69662
+ // because then at least half of the displayed digits will be leading 0s.
69663
+ DefaultUnitFormat.exponentialLowerBound = 0.000995;
69664
+ // Use exponential formatting for numbers whose magnitude cannot otherwise be displayed
69665
+ // with 6 digits or less.
69666
+ DefaultUnitFormat.exponentialUpperBound = 999999.5;
69749
69667
 
69750
69668
  /**
69751
- * Mapping configuration corresponding to a text mapping
69669
+ * Format for decimal numbers with units.
69752
69670
  */
69753
- class MappingTextConfig extends MappingConfig {
69671
+ class DecimalUnitFormat extends UnitFormat {
69672
+ constructor(locale, { minimumFractionDigits = 0, maximumFractionDigits = Math.max(3, minimumFractionDigits), unitScale = passthroughUnitScale } = {
69673
+ minimumFractionDigits: 0,
69674
+ maximumFractionDigits: 3,
69675
+ unitScale: passthroughUnitScale
69676
+ }) {
69677
+ super();
69678
+ this.scaledUnitFormatters = new Map();
69679
+ // Workaround to avoid a ts error about signDisplay not accepting the value 'negative'.
69680
+ // It has been supported by browsers since 8/23, but TypeScript still hasn't
69681
+ // added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269
69682
+ const signDisplay = 'negative';
69683
+ const intlNumberFormatOptions = {
69684
+ maximumFractionDigits,
69685
+ minimumFractionDigits,
69686
+ signDisplay
69687
+ };
69688
+ for (const scaledUnit of unitScale.supportedScaledUnits) {
69689
+ this.scaledUnitFormatters.set(scaledUnit.scaleFactor, scaledUnit.scaledUnitFormatFactory({
69690
+ locale,
69691
+ intlNumberFormatOptions
69692
+ }));
69693
+ }
69694
+ this.unitScale = unitScale;
69695
+ this.minimumFractionDigits = minimumFractionDigits;
69696
+ this.maximumFractionDigits = maximumFractionDigits;
69697
+ }
69698
+ resolvedOptions() {
69699
+ return {
69700
+ unitScale: this.unitScale,
69701
+ maximumFractionDigits: this.maximumFractionDigits,
69702
+ minimumFractionDigits: this.minimumFractionDigits
69703
+ };
69704
+ }
69705
+ tryFormat(number) {
69706
+ const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number);
69707
+ const scaledUnitFormatter = this.scaledUnitFormatters.get(scaledUnit.scaleFactor);
69708
+ return scaledUnitFormatter.format(scaledValue);
69709
+ }
69754
69710
  }
69755
69711
 
69756
69712
  /**
69757
- * The group header view for the icon column
69713
+ * Format for numbers (with optional units) in a number-text table column.
69758
69714
  */
69759
- class TableColumnIconGroupHeaderView extends TableColumnTextGroupHeaderViewBase {
69760
- constructor() {
69761
- super(...arguments);
69762
- this.textHidden = false;
69715
+ class NumberTextUnitFormat extends UnitFormat {
69716
+ constructor(locale, options) {
69717
+ super();
69718
+ this._resolvedOptions = this.resolveOptions(options);
69719
+ this.resolvedUnitFormat = this.resolveUnitFormat(locale, this._resolvedOptions);
69763
69720
  }
69764
- updateText() {
69765
- this.resetState();
69766
- if (!this.columnConfig) {
69767
- return;
69721
+ resolvedOptions() {
69722
+ return { ...this._resolvedOptions };
69723
+ }
69724
+ optionsMatch(targetOptions) {
69725
+ const targetResolvedOptions = this.resolveOptions(targetOptions);
69726
+ return (this._resolvedOptions.decimalDigits
69727
+ === targetResolvedOptions.decimalDigits
69728
+ && this._resolvedOptions.decimalMaximumDigits
69729
+ === targetResolvedOptions.decimalMaximumDigits
69730
+ && this._resolvedOptions.numberTextFormat
69731
+ === targetResolvedOptions.numberTextFormat
69732
+ && this._resolvedOptions.unitScale === targetResolvedOptions.unitScale);
69733
+ }
69734
+ tryFormat(number) {
69735
+ return this.resolvedUnitFormat.format(number);
69736
+ }
69737
+ resolveUnitFormat(locale, options) {
69738
+ const { numberTextFormat, decimalMaximumDigits, decimalDigits, unitScale } = options;
69739
+ if (numberTextFormat === NumberTextFormat.default) {
69740
+ return new DefaultUnitFormat(locale, {
69741
+ unitScale
69742
+ });
69768
69743
  }
69769
- const value = this.groupHeaderValue;
69770
- const mappingConfig = this.columnConfig.mappingConfigs.get(value);
69771
- if (mappingConfig instanceof MappingIconConfig) {
69772
- this.severity = mappingConfig.severity;
69773
- this.text = mappingConfig.text ?? '';
69774
- this.visualizationTemplate = mappingConfig.iconTemplate;
69744
+ if (typeof decimalDigits === 'number') {
69745
+ return new DecimalUnitFormat(locale, {
69746
+ minimumFractionDigits: decimalDigits,
69747
+ maximumFractionDigits: decimalDigits,
69748
+ unitScale
69749
+ });
69775
69750
  }
69776
- else if (mappingConfig instanceof MappingSpinnerConfig) {
69777
- this.text = mappingConfig.text ?? '';
69778
- this.visualizationTemplate = mappingConfig.spinnerTemplate;
69751
+ return new DecimalUnitFormat(locale, {
69752
+ minimumFractionDigits: 0,
69753
+ maximumFractionDigits: decimalMaximumDigits,
69754
+ unitScale
69755
+ });
69756
+ }
69757
+ resolveOptions(options) {
69758
+ if (!options || options.numberTextFormat === NumberTextFormat.default) {
69759
+ return {
69760
+ numberTextFormat: NumberTextFormat.default,
69761
+ decimalDigits: undefined,
69762
+ decimalMaximumDigits: undefined,
69763
+ unitScale: options?.unitScale ?? passthroughUnitScale
69764
+ };
69779
69765
  }
69780
- else if (mappingConfig instanceof MappingTextConfig) {
69781
- this.text = mappingConfig.text ?? '';
69766
+ const hasDecimalDigits = typeof options.decimalDigits === 'number';
69767
+ const hasDecimalMaximumDigits = typeof options.decimalMaximumDigits === 'number';
69768
+ if (hasDecimalDigits && hasDecimalMaximumDigits) {
69769
+ throw new Error('decimalDigits and decimalMaximumDigits are mutually exclusive. Do not specify both.');
69782
69770
  }
69783
- }
69784
- resetState() {
69785
- this.text = '';
69786
- this.visualizationTemplate = undefined;
69787
- this.severity = IconSeverity.default;
69771
+ if (!hasDecimalDigits && !hasDecimalMaximumDigits) {
69772
+ return {
69773
+ numberTextFormat: NumberTextFormat.decimal,
69774
+ decimalDigits: NumberTextUnitFormat.defaultDecimalDigits,
69775
+ decimalMaximumDigits: undefined,
69776
+ unitScale: options.unitScale ?? passthroughUnitScale
69777
+ };
69778
+ }
69779
+ return {
69780
+ numberTextFormat: NumberTextFormat.decimal,
69781
+ decimalDigits: options.decimalDigits,
69782
+ decimalMaximumDigits: options.decimalMaximumDigits,
69783
+ unitScale: options.unitScale ?? passthroughUnitScale
69784
+ };
69788
69785
  }
69789
69786
  }
69790
- __decorate$1([
69791
- observable
69792
- ], TableColumnIconGroupHeaderView.prototype, "severity", void 0);
69793
- __decorate$1([
69794
- observable
69795
- ], TableColumnIconGroupHeaderView.prototype, "visualizationTemplate", void 0);
69796
- const iconGroupHeaderView = TableColumnIconGroupHeaderView.compose({
69797
- baseName: 'table-column-icon-group-header-view',
69798
- template: template$7,
69799
- styles: styles$9
69800
- });
69801
- DesignSystem.getOrCreate().withPrefix('nimble').register(iconGroupHeaderView());
69802
- const tableColumnIconGroupHeaderViewTag = 'nimble-table-column-icon-group-header-view';
69803
-
69804
- const styles$8 = css `
69805
- ${display('inline-flex')}
69806
-
69807
- :host {
69808
- gap: ${smallPadding};
69809
- align-items: center;
69810
- }
69811
-
69812
- .reserve-icon-size {
69813
- flex-shrink: 0;
69814
- width: ${iconSize};
69815
- height: ${iconSize};
69816
- }
69817
-
69818
- .text {
69819
- flex-shrink: 1;
69820
- font: ${bodyFont};
69821
- color: ${bodyFontColor};
69822
- white-space: nowrap;
69823
- overflow: hidden;
69824
- text-overflow: ellipsis;
69825
- }
69826
- `;
69827
-
69828
- // prettier-ignore
69829
- const template$6 = html `
69830
- ${when(x => x.visualizationTemplate, html `
69831
- <span class="reserve-icon-size">
69832
- ${x => x.visualizationTemplate}
69833
- </span>
69834
- `)}
69835
- ${when(x => !x.textHidden, html `
69836
- <span
69837
- ${overflow('hasOverflow')}
69838
- title=${x => (x.hasOverflow && x.text ? x.text : null)}
69839
- class="text"
69840
- >
69841
- ${x => x.text}
69842
- </span>
69843
- `)}
69844
- `;
69787
+ NumberTextUnitFormat.defaultDecimalDigits = 2;
69845
69788
 
69789
+ const numberTextValidityFlagNames = [
69790
+ 'invalidDecimalDigits',
69791
+ 'invalidDecimalMaximumDigits',
69792
+ 'decimalDigitsMutuallyExclusiveWithDecimalMaximumDigits',
69793
+ 'moreThanOneUnitSpecified'
69794
+ ];
69795
+ // The maximum and minimum allowed configuration for 'maximumFractionDigits'
69796
+ // and 'minimumFractionDigits' on the NumberFormat.
69797
+ const minimumValidDecimalDigits = 0;
69798
+ const maximumValidDecimalDigits = 20;
69846
69799
  /**
69847
- * The cell view for the icon column
69800
+ * Validator for TableColumnNumberText.
69848
69801
  */
69849
- class TableColumnIconCellView extends TableCellView {
69802
+ class TableColumnNumberTextValidator extends ColumnValidator {
69850
69803
  constructor() {
69851
- super(...arguments);
69852
- this.textHidden = false;
69853
- /** @internal */
69854
- this.hasOverflow = false;
69804
+ super(numberTextValidityFlagNames);
69855
69805
  }
69856
- columnConfigChanged() {
69857
- this.updateState();
69806
+ validateDecimalDigits(format, decimalDigits) {
69807
+ const shouldValidateDecimalDigitsValue = format === NumberTextFormat.decimal
69808
+ && typeof decimalDigits === 'number';
69809
+ const invalid = shouldValidateDecimalDigitsValue
69810
+ ? this.isInvalidDecimalDigitsValue(decimalDigits)
69811
+ : false;
69812
+ this.setConditionValue('invalidDecimalDigits', invalid);
69858
69813
  }
69859
- cellRecordChanged() {
69860
- this.updateState();
69814
+ validateDecimalMaximumDigits(format, decimalMaximumDigits) {
69815
+ const shouldValidateDecimalDigitsValue = format === NumberTextFormat.decimal
69816
+ && typeof decimalMaximumDigits === 'number';
69817
+ const invalid = shouldValidateDecimalDigitsValue
69818
+ ? this.isInvalidDecimalDigitsValue(decimalMaximumDigits)
69819
+ : false;
69820
+ this.setConditionValue('invalidDecimalMaximumDigits', invalid);
69861
69821
  }
69862
- updateState() {
69863
- this.resetState();
69864
- if (!this.columnConfig || !this.cellRecord) {
69865
- return;
69866
- }
69867
- const value = this.cellRecord.value;
69868
- if (value === undefined || value === null) {
69869
- return;
69870
- }
69871
- const mappingConfig = this.columnConfig.mappingConfigs.get(value);
69872
- if (mappingConfig instanceof MappingIconConfig) {
69873
- this.severity = mappingConfig.severity;
69874
- this.text = mappingConfig.text;
69875
- this.visualizationTemplate = mappingConfig.iconTemplate;
69876
- this.textHidden = mappingConfig.textHidden;
69877
- }
69878
- else if (mappingConfig instanceof MappingSpinnerConfig) {
69879
- this.text = mappingConfig.text;
69880
- this.visualizationTemplate = mappingConfig.spinnerTemplate;
69881
- this.textHidden = mappingConfig.textHidden;
69882
- }
69883
- else if (mappingConfig instanceof MappingTextConfig) {
69884
- this.text = mappingConfig.text;
69885
- this.textHidden = false;
69886
- }
69822
+ validateNoMutuallyExclusiveProperties(format, decimalDigits, decimalMaximumDigits) {
69823
+ const shouldValidateMutuallyExclusiveProperties = format === NumberTextFormat.decimal;
69824
+ const invalid = shouldValidateMutuallyExclusiveProperties
69825
+ ? typeof decimalDigits === 'number'
69826
+ && typeof decimalMaximumDigits === 'number'
69827
+ : false;
69828
+ this.setConditionValue('decimalDigitsMutuallyExclusiveWithDecimalMaximumDigits', invalid);
69887
69829
  }
69888
- resetState() {
69889
- this.text = undefined;
69890
- this.textHidden = false;
69891
- this.visualizationTemplate = undefined;
69892
- this.severity = IconSeverity.default;
69830
+ validateAtMostOneUnit(unitElements) {
69831
+ const invalid = unitElements.length > 1;
69832
+ this.setConditionValue('moreThanOneUnitSpecified', invalid);
69833
+ }
69834
+ isInvalidDecimalDigitsValue(decimalDigits) {
69835
+ return (decimalDigits < minimumValidDecimalDigits
69836
+ || decimalDigits > maximumValidDecimalDigits);
69893
69837
  }
69894
69838
  }
69895
- __decorate$1([
69896
- observable
69897
- ], TableColumnIconCellView.prototype, "severity", void 0);
69898
- __decorate$1([
69899
- observable
69900
- ], TableColumnIconCellView.prototype, "text", void 0);
69901
- __decorate$1([
69902
- observable
69903
- ], TableColumnIconCellView.prototype, "visualizationTemplate", void 0);
69904
- __decorate$1([
69905
- observable
69906
- ], TableColumnIconCellView.prototype, "textHidden", void 0);
69907
- __decorate$1([
69908
- observable
69909
- ], TableColumnIconCellView.prototype, "hasOverflow", void 0);
69910
- const iconCellView = TableColumnIconCellView.compose({
69911
- baseName: 'table-column-icon-cell-view',
69912
- template: template$6,
69913
- styles: styles$8
69914
- });
69915
- DesignSystem.getOrCreate().withPrefix('nimble').register(iconCellView());
69916
- const tableColumnIconCellViewTag = 'nimble-table-column-icon-cell-view';
69917
69839
 
69918
69840
  /**
69919
- * Width mode for the icon column
69841
+ * The base class for an element that provides a unit scale for scaled unit conversions
69920
69842
  */
69921
- const TableColumnMappingWidthMode = {
69922
- default: undefined,
69923
- iconSize: 'icon-size'
69924
- };
69843
+ class Unit extends FoundationElement {
69844
+ }
69845
+ __decorate$1([
69846
+ observable
69847
+ ], Unit.prototype, "resolvedUnitScale", void 0);
69925
69848
 
69926
69849
  /**
69927
- * Table column that maps values to icons / spinners
69850
+ * The table column for displaying numbers as text.
69928
69851
  */
69929
- class TableColumnIcon extends mixinGroupableColumnAPI(mixinFractionalWidthColumnAPI((TableColumnEnumBase))) {
69930
- minPixelWidthChanged() {
69931
- if (this.widthMode !== TableColumnMappingWidthMode.iconSize) {
69932
- this.columnInternals.minPixelWidth = this.getConfiguredMinPixelWidth();
69933
- }
69852
+ class TableColumnNumberText extends mixinTextBase((TableColumnTextBase)) {
69853
+ constructor() {
69854
+ super(...arguments);
69855
+ this.langSubscriber = {
69856
+ handleChange: () => {
69857
+ this.updateColumnConfig();
69858
+ }
69859
+ };
69860
+ this.unitSubscriber = {
69861
+ handleChange: () => {
69862
+ this.updateColumnConfig();
69863
+ }
69864
+ };
69865
+ }
69866
+ connectedCallback() {
69867
+ super.connectedCallback();
69868
+ lang.subscribe(this.langSubscriber, this);
69869
+ this.updateColumnConfig();
69870
+ }
69871
+ disconnectedCallback() {
69872
+ super.disconnectedCallback();
69873
+ lang.unsubscribe(this.langSubscriber, this);
69874
+ }
69875
+ placeholderChanged() {
69876
+ this.updateColumnConfig();
69934
69877
  }
69935
69878
  getColumnInternalsOptions() {
69936
69879
  return {
69937
69880
  cellRecordFieldNames: ['value'],
69938
- cellViewTag: tableColumnIconCellViewTag,
69939
- groupHeaderViewTag: tableColumnIconGroupHeaderViewTag,
69881
+ cellViewTag: tableColumnNumberTextCellViewTag,
69882
+ groupHeaderViewTag: tableColumnNumberTextGroupHeaderTag,
69940
69883
  delegatedEvents: [],
69941
69884
  sortOperation: TableColumnSortOperation.basic,
69942
- validator: new TableColumnIconValidator()
69943
- };
69944
- }
69945
- createColumnConfig(mappingConfigs) {
69946
- return {
69947
- mappingConfigs
69885
+ validator: new TableColumnNumberTextValidator()
69948
69886
  };
69949
69887
  }
69950
- createMappingConfig(mapping) {
69951
- if (mapping instanceof MappingIcon) {
69952
- return new MappingIconConfig(mapping.resolvedIcon, mapping.severity, mapping.text, mapping.textHidden);
69888
+ updateUnitNotifier() {
69889
+ if (this.unitNotifier) {
69890
+ this.unitNotifier.unsubscribe(this.unitSubscriber);
69891
+ this.unitNotifier = undefined;
69953
69892
  }
69954
- if (mapping instanceof MappingSpinner) {
69955
- return new MappingSpinnerConfig(mapping.text, mapping.textHidden);
69893
+ if (this.unit) {
69894
+ const notifier = Observable.getNotifier(this.unit);
69895
+ notifier.subscribe(this.unitSubscriber, 'resolvedUnitScale');
69896
+ this.unitNotifier = notifier;
69956
69897
  }
69957
- if (mapping instanceof MappingText) {
69958
- return new MappingTextConfig(mapping.text);
69898
+ }
69899
+ formatChanged() {
69900
+ this.updateColumnConfig();
69901
+ }
69902
+ alignmentChanged() {
69903
+ this.updateColumnConfig();
69904
+ }
69905
+ decimalDigitsChanged() {
69906
+ this.updateColumnConfig();
69907
+ }
69908
+ decimalMaximumDigitsChanged() {
69909
+ this.updateColumnConfig();
69910
+ }
69911
+ unitElementsChanged() {
69912
+ void this.updateUnit();
69913
+ }
69914
+ async updateUnit() {
69915
+ this.unit = undefined;
69916
+ if (this.unitElements) {
69917
+ await waitUntilCustomElementsDefinedAsync(this.unitElements);
69918
+ this.unit = this.unitElements.find((x) => x instanceof Unit);
69959
69919
  }
69960
- // Getting here would indicate a programming error, b/c validation will prevent
69961
- // this function from running when there is an unsupported mapping.
69962
- throw new Error('Unsupported mapping');
69920
+ this.updateUnitNotifier();
69963
69921
  }
69964
- widthModeChanged() {
69965
- if (this.widthMode === TableColumnMappingWidthMode.iconSize) {
69966
- this.columnInternals.resizingDisabled = true;
69967
- this.columnInternals.hideHeaderIndicators = true;
69968
- this.columnInternals.pixelWidth = singleIconColumnWidth;
69969
- this.columnInternals.minPixelWidth = singleIconColumnWidth;
69922
+ unitChanged() {
69923
+ this.updateColumnConfig();
69924
+ }
69925
+ updateColumnConfig() {
69926
+ const validator = this.columnInternals.validator;
69927
+ validator.validateDecimalDigits(this.format, this.decimalDigits);
69928
+ validator.validateDecimalMaximumDigits(this.format, this.decimalMaximumDigits);
69929
+ validator.validateNoMutuallyExclusiveProperties(this.format, this.decimalDigits, this.decimalMaximumDigits);
69930
+ validator.validateAtMostOneUnit(this.unitElements ?? []);
69931
+ if (validator.isValid()) {
69932
+ const columnConfig = {
69933
+ formatter: this.createFormatter(),
69934
+ alignment: this.determineCellContentAlignment(),
69935
+ placeholder: this.placeholder
69936
+ };
69937
+ this.columnInternals.columnConfig = columnConfig;
69970
69938
  }
69971
69939
  else {
69972
- this.columnInternals.resizingDisabled = false;
69973
- this.columnInternals.hideHeaderIndicators = false;
69974
- this.columnInternals.pixelWidth = undefined;
69975
- this.columnInternals.minPixelWidth = this.getConfiguredMinPixelWidth();
69940
+ this.columnInternals.columnConfig = undefined;
69976
69941
  }
69977
69942
  }
69978
- getConfiguredMinPixelWidth() {
69979
- if (typeof this.minPixelWidth === 'number') {
69980
- return this.minPixelWidth;
69943
+ createFormatter() {
69944
+ const unitScale = this.unit?.resolvedUnitScale;
69945
+ return new NumberTextUnitFormat(lang.getValueFor(this), {
69946
+ // Attribute values sometimes resolve to either null or undefined
69947
+ // See https://github.com/microsoft/fast/issues/6630
69948
+ numberTextFormat: this.format ?? undefined,
69949
+ decimalDigits: this.decimalDigits ?? undefined,
69950
+ decimalMaximumDigits: this.decimalMaximumDigits ?? undefined,
69951
+ unitScale
69952
+ });
69953
+ }
69954
+ determineCellContentAlignment() {
69955
+ if (this.alignment === NumberTextAlignment.left) {
69956
+ return TextCellViewBaseAlignment.left;
69981
69957
  }
69982
- return defaultMinPixelWidth;
69958
+ if (this.alignment === NumberTextAlignment.right) {
69959
+ return TextCellViewBaseAlignment.right;
69960
+ }
69961
+ // Look at format and decimal max digits and unit to determine the default alignment
69962
+ if (this.format === NumberTextFormat.decimal
69963
+ && typeof this.decimalMaximumDigits !== 'number'
69964
+ && !this.unit) {
69965
+ return TextCellViewBaseAlignment.right;
69966
+ }
69967
+ return TextCellViewBaseAlignment.left;
69983
69968
  }
69984
69969
  }
69985
69970
  __decorate$1([
69986
- attr({ attribute: 'width-mode' })
69987
- ], TableColumnIcon.prototype, "widthMode", void 0);
69988
- const nimbleTableColumnIcon = TableColumnIcon.compose({
69989
- baseName: 'table-column-icon',
69990
- template: template$8,
69991
- styles: styles$a
69971
+ attr
69972
+ ], TableColumnNumberText.prototype, "format", void 0);
69973
+ __decorate$1([
69974
+ attr
69975
+ ], TableColumnNumberText.prototype, "alignment", void 0);
69976
+ __decorate$1([
69977
+ attr({ attribute: 'decimal-digits', converter: nullableNumberConverter })
69978
+ ], TableColumnNumberText.prototype, "decimalDigits", void 0);
69979
+ __decorate$1([
69980
+ attr({
69981
+ attribute: 'decimal-maximum-digits',
69982
+ converter: nullableNumberConverter
69983
+ })
69984
+ ], TableColumnNumberText.prototype, "decimalMaximumDigits", void 0);
69985
+ __decorate$1([
69986
+ observable
69987
+ ], TableColumnNumberText.prototype, "unitElements", void 0);
69988
+ __decorate$1([
69989
+ observable
69990
+ ], TableColumnNumberText.prototype, "unit", void 0);
69991
+ const nimbleTableColumnNumberText = TableColumnNumberText.compose({
69992
+ baseName: 'table-column-number-text',
69993
+ template: template$6,
69994
+ styles: styles$e
69992
69995
  });
69993
69996
  DesignSystem.getOrCreate()
69994
69997
  .withPrefix('nimble')
69995
- .register(nimbleTableColumnIcon());
69998
+ .register(nimbleTableColumnNumberText());
69996
69999
 
69997
70000
  /**
69998
70001
  * A cell view for displaying string fields as text