@ifsworld/granite-components 3.0.0 → 3.3.0

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 (76) hide show
  1. package/bundles/ifsworld-granite-components.umd.js +444 -4
  2. package/bundles/ifsworld-granite-components.umd.js.map +1 -1
  3. package/bundles/ifsworld-granite-components.umd.min.js +3 -3
  4. package/bundles/ifsworld-granite-components.umd.min.js.map +1 -1
  5. package/esm2015/ifsworld-granite-components.js +5 -0
  6. package/esm2015/ifsworld-granite-components.js.map +1 -1
  7. package/esm2015/ifsworld-granite-components.metadata.json +1 -1
  8. package/esm2015/index.js +7 -0
  9. package/esm2015/index.js.map +1 -1
  10. package/esm2015/index.metadata.json +1 -1
  11. package/esm2015/lib/core/pipes/pure-pipes.module.js +11 -0
  12. package/esm2015/lib/core/pipes/pure-pipes.module.js.map +1 -0
  13. package/esm2015/lib/core/pipes/pure-pipes.module.metadata.json +1 -0
  14. package/esm2015/lib/core/pipes/title.pipe.js +17 -0
  15. package/esm2015/lib/core/pipes/title.pipe.js.map +1 -0
  16. package/esm2015/lib/core/pipes/title.pipe.metadata.json +1 -0
  17. package/esm2015/lib/core/types.js +2 -0
  18. package/esm2015/lib/core/types.js.map +1 -0
  19. package/esm2015/lib/core/types.metadata.json +1 -0
  20. package/esm2015/lib/input-field/input-field.component.js +144 -0
  21. package/esm2015/lib/input-field/input-field.component.js.map +1 -0
  22. package/esm2015/lib/input-field/input-field.component.metadata.json +1 -0
  23. package/esm2015/lib/input-field/input-field.module.js +15 -0
  24. package/esm2015/lib/input-field/input-field.module.js.map +1 -0
  25. package/esm2015/lib/input-field/input-field.module.metadata.json +1 -0
  26. package/esm2015/lib/radio-button/radio-button.component.js +109 -0
  27. package/esm2015/lib/radio-button/radio-button.component.js.map +1 -0
  28. package/esm2015/lib/radio-button/radio-button.component.metadata.json +1 -0
  29. package/esm2015/lib/radio-button/radio-button.module.js +11 -0
  30. package/esm2015/lib/radio-button/radio-button.module.js.map +1 -0
  31. package/esm2015/lib/radio-button/radio-button.module.metadata.json +1 -0
  32. package/esm2015/lib/table/cell/cell.js +12 -0
  33. package/esm2015/lib/table/cell/cell.js.map +1 -0
  34. package/esm2015/lib/table/cell/cell.metadata.json +1 -0
  35. package/esm2015/lib/table/cell/table-data-cell.component.js +20 -0
  36. package/esm2015/lib/table/cell/table-data-cell.component.js.map +1 -0
  37. package/esm2015/lib/table/cell/table-data-cell.component.metadata.json +1 -0
  38. package/esm2015/lib/table/cell/table-header-cell.component.js +13 -0
  39. package/esm2015/lib/table/cell/table-header-cell.component.js.map +1 -0
  40. package/esm2015/lib/table/cell/table-header-cell.component.metadata.json +1 -0
  41. package/esm2015/lib/table/column/table-column.directive.js +16 -0
  42. package/esm2015/lib/table/column/table-column.directive.js.map +1 -0
  43. package/esm2015/lib/table/column/table-column.directive.metadata.json +1 -0
  44. package/esm2015/lib/table/table-constants.library.js +4 -0
  45. package/esm2015/lib/table/table-constants.library.js.map +1 -0
  46. package/esm2015/lib/table/table-constants.library.metadata.json +1 -0
  47. package/esm2015/lib/table/table.component.js +28 -0
  48. package/esm2015/lib/table/table.component.js.map +1 -0
  49. package/esm2015/lib/table/table.component.metadata.json +1 -0
  50. package/esm2015/lib/table/table.module.js +24 -0
  51. package/esm2015/lib/table/table.module.js.map +1 -0
  52. package/esm2015/lib/table/table.module.metadata.json +1 -0
  53. package/esm2015/lib/toggle-switch/toggle-switch.component.js.map +1 -1
  54. package/esm2015/lib/toggle-switch/toggle-switch.component.metadata.json +1 -1
  55. package/fesm2015/ifsworld-granite-components.js +392 -2
  56. package/fesm2015/ifsworld-granite-components.js.map +1 -1
  57. package/ifsworld-granite-components.d.ts +5 -0
  58. package/ifsworld-granite-components.metadata.json +1 -1
  59. package/index.d.ts +7 -0
  60. package/lib/core/pipes/pure-pipes.module.d.ts +2 -0
  61. package/lib/core/pipes/title.pipe.d.ts +4 -0
  62. package/lib/core/types.d.ts +1 -0
  63. package/lib/input-field/input-field.component.d.ts +39 -0
  64. package/lib/input-field/input-field.module.d.ts +2 -0
  65. package/lib/radio-button/radio-button.component.d.ts +32 -0
  66. package/lib/radio-button/radio-button.module.d.ts +2 -0
  67. package/lib/table/cell/cell.d.ts +5 -0
  68. package/lib/table/cell/table-data-cell.component.d.ts +10 -0
  69. package/lib/table/cell/table-header-cell.component.d.ts +3 -0
  70. package/lib/table/column/table-column.directive.d.ts +13 -0
  71. package/lib/table/table-constants.library.d.ts +3 -0
  72. package/lib/table/table.component.d.ts +16 -0
  73. package/lib/table/table.module.d.ts +2 -0
  74. package/lib/toggle-switch/toggle-switch.component.d.ts +1 -1
  75. package/package.json +1 -1
  76. package/src/lib/core/style/_responsive.scss +1 -0
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/cdk/coercion'), require('rxjs'), require('rxjs/operators'), require('@angular/cdk/testing'), require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/cdk/a11y'), require('@angular/cdk/keycodes'), require('@angular/animations'), require('@angular/cdk/bidi'), require('@angular/cdk/platform')) :
3
- typeof define === 'function' && define.amd ? define('@ifsworld/granite-components', ['exports', '@angular/core', '@angular/common', '@angular/cdk/coercion', 'rxjs', 'rxjs/operators', '@angular/cdk/testing', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/cdk/a11y', '@angular/cdk/keycodes', '@angular/animations', '@angular/cdk/bidi', '@angular/cdk/platform'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.ifsworld = global.ifsworld || {}, global.ifsworld['granite-components'] = {}), global.ng.core, global.ng.common, global.ng.cdk.coercion, global.rxjs, global.rxjs.operators, global.ng.cdk.testing, global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.cdk.a11y, global.ng.cdk.keycodes, global.ng.animations, global.ng.cdk.bidi, global.ng.cdk.platform));
5
- }(this, (function (exports, core, common, coercion, rxjs, operators, testing, overlay, portal, a11y, keycodes, animations, bidi, platform) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/cdk/coercion'), require('rxjs'), require('rxjs/operators'), require('@angular/cdk/testing'), require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/cdk/a11y'), require('@angular/cdk/keycodes'), require('@angular/animations'), require('@angular/cdk/bidi'), require('@angular/cdk/platform'), require('@angular/cdk/collections'), require('@angular/cdk/table')) :
3
+ typeof define === 'function' && define.amd ? define('@ifsworld/granite-components', ['exports', '@angular/core', '@angular/common', '@angular/cdk/coercion', 'rxjs', 'rxjs/operators', '@angular/cdk/testing', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/cdk/a11y', '@angular/cdk/keycodes', '@angular/animations', '@angular/cdk/bidi', '@angular/cdk/platform', '@angular/cdk/collections', '@angular/cdk/table'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.ifsworld = global.ifsworld || {}, global.ifsworld['granite-components'] = {}), global.ng.core, global.ng.common, global.ng.cdk.coercion, global.rxjs, global.rxjs.operators, global.ng.cdk.testing, global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.cdk.a11y, global.ng.cdk.keycodes, global.ng.animations, global.ng.cdk.bidi, global.ng.cdk.platform, global.ng.cdk.collections, global.ng.cdk.table));
5
+ }(this, (function (exports, core, common, coercion, rxjs, operators, testing, overlay, portal, a11y, keycodes, animations, bidi, platform, collections, table) { 'use strict';
6
6
 
7
7
  var GraniteArrangeGridItemComponent = /** @class */ (function () {
8
8
  function GraniteArrangeGridItemComponent(element) {
@@ -2659,6 +2659,126 @@
2659
2659
  },] }
2660
2660
  ];
2661
2661
 
2662
+ var GraniteRadioButtonComponent = /** @class */ (function () {
2663
+ function GraniteRadioButtonComponent(_focusMonitor, _radioDispatcher) {
2664
+ var _this = this;
2665
+ this._focusMonitor = _focusMonitor;
2666
+ this._radioDispatcher = _radioDispatcher;
2667
+ this.id = null;
2668
+ this.checked = false;
2669
+ this.required = false;
2670
+ this.disabled = false;
2671
+ this.readonly = false;
2672
+ this.labelPosition = 'after';
2673
+ this.ariaLabel = null;
2674
+ this.ariaLabelledby = null;
2675
+ this.radioChange = new core.EventEmitter();
2676
+ this.radioBlur = new core.EventEmitter();
2677
+ this._positionBefore = false;
2678
+ this._radioDisabled = false;
2679
+ this._removeUniqueSelectionListener = function () {
2680
+ // Function used to deregister listener
2681
+ };
2682
+ this._removeUniqueSelectionListener = _radioDispatcher.listen(function (id, name) {
2683
+ if (id !== _this.id && name === _this.name) {
2684
+ _this.checked = false;
2685
+ }
2686
+ });
2687
+ }
2688
+ GraniteRadioButtonComponent.prototype.ngOnChanges = function (changes) {
2689
+ if (changes.checked) {
2690
+ this.checked = coercion.coerceBooleanProperty(changes.checked.currentValue);
2691
+ }
2692
+ if (changes.required) {
2693
+ this.required = coercion.coerceBooleanProperty(changes.required.currentValue);
2694
+ }
2695
+ if (changes.readonly) {
2696
+ this.readonly = coercion.coerceBooleanProperty(changes.readonly.currentValue);
2697
+ }
2698
+ if (changes.disabled) {
2699
+ this.disabled = coercion.coerceBooleanProperty(changes.disabled.currentValue);
2700
+ }
2701
+ if (changes.labelPosition != null) {
2702
+ this._positionBefore =
2703
+ changes.labelPosition.currentValue != null &&
2704
+ changes.labelPosition.currentValue === 'before';
2705
+ }
2706
+ if ((changes.disabled || changes.readonly) &&
2707
+ (this.disabled || this.readonly)) {
2708
+ this._radioDisabled = true;
2709
+ }
2710
+ };
2711
+ GraniteRadioButtonComponent.prototype.ngOnDestroy = function () {
2712
+ this._removeUniqueSelectionListener();
2713
+ };
2714
+ // Focuses the radio button.
2715
+ GraniteRadioButtonComponent.prototype.focus = function (origin, options) {
2716
+ if (origin === void 0) { origin = 'program'; }
2717
+ this._focusMonitor.focusVia(this._getInputElement(), origin, options);
2718
+ };
2719
+ GraniteRadioButtonComponent.prototype._radioClick = function (targetValue) {
2720
+ this.radioChange.emit(targetValue);
2721
+ };
2722
+ GraniteRadioButtonComponent.prototype._radioChange = function () {
2723
+ this.checked = this._getInputElement().checked;
2724
+ // Notify all radio buttons with the same name to un-check
2725
+ this._radioDispatcher.notify(this.id, this.name);
2726
+ };
2727
+ GraniteRadioButtonComponent.prototype._onBlur = function () {
2728
+ this.radioBlur.emit();
2729
+ };
2730
+ GraniteRadioButtonComponent.prototype._getInputElement = function () {
2731
+ return this._inputElement.nativeElement;
2732
+ };
2733
+ return GraniteRadioButtonComponent;
2734
+ }());
2735
+ GraniteRadioButtonComponent.decorators = [
2736
+ { type: core.Component, args: [{
2737
+ selector: 'granite-radio-button',
2738
+ exportAs: 'graniteRadioButton',
2739
+ host: {
2740
+ class: 'granite-radio-button',
2741
+ '[class.granite-radio-button-disabled]': 'disabled',
2742
+ '[class.granite-radio-button-label-before]': '_positionBefore',
2743
+ '[class.granite-radio-button-checked]': 'checked',
2744
+ '[class.granite-radio-button-readonly]': 'readonly',
2745
+ },
2746
+ template: "<label class=\"granite-radio-button-label\">\n <div class=\"granite-radio-button-outer-circle\">\n <input\n #input\n [id]=\"id\"\n class=\"granite-radio-button-input cdk-visually-hidden\"\n type=\"radio\"\n [attr.name]=\"name\"\n [disabled]=\"_radioDisabled\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n [attr.aria-checked]=\"checked.toString()\"\n [value]=\"value\"\n (click)=\"_radioClick($event.target.value)\"\n (change)=\"_radioChange()\"\n (blur)=\"_onBlur()\"\n />\n <div class=\"granite-radio-button-inner-circle\"></div>\n </div>\n <span class=\"granite-radio-button-text\">\n <ng-content></ng-content>\n </span>\n</label>\n",
2747
+ styles: [".cdk-visually-hidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap;outline:0;-webkit-appearance:none;-moz-appearance:none}:host(.granite-radio-button):not(.granite-radio-button-readonly):not(.granite-radio-button-disabled) .granite-radio-button-label:hover{cursor:pointer}:host(.granite-radio-button):not(.granite-radio-button-readonly):not(.granite-radio-button-disabled) .granite-radio-button-label:hover .granite-radio-button-outer-circle{border-color:var(--granite-color-background-active-hover)}:host(.granite-radio-button):not(.granite-radio-button-readonly):not(.granite-radio-button-disabled) .granite-radio-button-label:hover .granite-radio-button-inner-circle{background-color:var(--granite-color-background-active-hover)}:host(.granite-radio-button):not(.granite-radio-button-readonly):not(.granite-radio-button-disabled) .granite-radio-button-outer-circle:focus-within{border:1px solid var(--granite-color-focus)}:host(.granite-radio-button-label-before) .granite-radio-button-label{flex-direction:row-reverse}:host(.granite-radio-button-label-before) .granite-radio-button-text{-webkit-padding-start:0;padding-inline-start:0;-webkit-padding-end:var(--granite-spacing-s);padding-inline-end:var(--granite-spacing-s)}:host(.granite-radio-button-checked) .granite-radio-button-inner-circle{visibility:visible;-webkit-animation:growAnimation .1s;animation:growAnimation .1s}:host(.granite-radio-button-checked) .granite-radio-button-outer-circle{border-color:var(--granite-color-background-active)}:host(.granite-radio-button-checked).granite-radio-button-disabled .granite-radio-button-inner-circle{background-color:var(--granite-color-text)}:host(.granite-radio-button-checked).granite-radio-button-disabled .granite-radio-button-outer-circle{border:1px solid var(--granite-color-text);background-color:transparent}:host(.granite-radio-button-checked).granite-radio-button-readonly .granite-radio-button-inner-circle{background-color:var(--granite-color-text)}:host(.granite-radio-button-checked).granite-radio-button-readonly .granite-radio-button-outer-circle{border:1px solid var(--granite-color-text)}.granite-radio-button-text{-webkit-padding-start:var(--granite-spacing-s);padding-inline-start:var(--granite-spacing-s);color:var(--granite-color-text)}.granite-radio-button-text:empty{display:none}.granite-radio-button-label{display:flex;align-items:center;width:-webkit-max-content;width:-moz-max-content;width:max-content;-webkit-margin-end:var(--granite-spacing-xl);margin-inline-end:var(--granite-spacing-xl)}:host(.granite-radio-button-disabled) .granite-radio-button-text{opacity:.6}:host(.granite-radio-button-disabled) .granite-radio-button-outer-circle{opacity:.3;background-color:var(--granite-color-border-soft)}.granite-radio-button-outer-circle{height:var(--granite-spacing-m);width:var(--granite-spacing-m);border-radius:50%;box-sizing:border-box;border:1px solid var(--granite-color-border-hard);display:flex;justify-content:center;position:relative;align-items:center}.granite-radio-button-inner-circle{width:calc(var(--granite-spacing-s) * 1.25);height:calc(var(--granite-spacing-s) * 1.25);visibility:hidden;position:absolute;border-radius:50%;background-color:var(--granite-color-background-active);margin:auto}@-webkit-keyframes growAnimation{0%{transform:scale(0)}to{transform:scale(1)}}@keyframes growAnimation{0%{transform:scale(0)}to{transform:scale(1)}}"]
2748
+ },] }
2749
+ ];
2750
+ GraniteRadioButtonComponent.ctorParameters = function () { return [
2751
+ { type: a11y.FocusMonitor },
2752
+ { type: collections.UniqueSelectionDispatcher }
2753
+ ]; };
2754
+ GraniteRadioButtonComponent.propDecorators = {
2755
+ value: [{ type: core.Input }],
2756
+ id: [{ type: core.Input }],
2757
+ name: [{ type: core.Input }],
2758
+ checked: [{ type: core.Input }],
2759
+ required: [{ type: core.Input }],
2760
+ disabled: [{ type: core.Input }],
2761
+ readonly: [{ type: core.Input }],
2762
+ labelPosition: [{ type: core.Input }],
2763
+ ariaLabel: [{ type: core.Input, args: ['aria-label',] }],
2764
+ ariaLabelledby: [{ type: core.Input, args: ['aria-labelledby',] }],
2765
+ radioChange: [{ type: core.Output }],
2766
+ radioBlur: [{ type: core.Output }],
2767
+ _inputElement: [{ type: core.ViewChild, args: ['input',] }]
2768
+ };
2769
+
2770
+ var GraniteRadioButtonModule = /** @class */ (function () {
2771
+ function GraniteRadioButtonModule() {
2772
+ }
2773
+ return GraniteRadioButtonModule;
2774
+ }());
2775
+ GraniteRadioButtonModule.decorators = [
2776
+ { type: core.NgModule, args: [{
2777
+ declarations: [GraniteRadioButtonComponent],
2778
+ exports: [GraniteRadioButtonComponent],
2779
+ },] }
2780
+ ];
2781
+
2662
2782
  var disabledMixin = function (Base) {
2663
2783
  if (Base === void 0) { Base = /** @class */ (function () {
2664
2784
  function class_1() {
@@ -2805,6 +2925,314 @@
2805
2925
  },] }
2806
2926
  ];
2807
2927
 
2928
+ var GraniteTableColumnDirective = /** @class */ (function () {
2929
+ function GraniteTableColumnDirective() {
2930
+ }
2931
+ return GraniteTableColumnDirective;
2932
+ }());
2933
+ GraniteTableColumnDirective.decorators = [
2934
+ { type: core.Directive, args: [{
2935
+ // eslint-disable-next-line @angular-eslint/directive-selector
2936
+ selector: 'granite-table-column',
2937
+ },] }
2938
+ ];
2939
+ GraniteTableColumnDirective.propDecorators = {
2940
+ name: [{ type: core.Input }],
2941
+ title: [{ type: core.Input }],
2942
+ staticType: [{ type: core.Input }],
2943
+ tableCellTemplateRef: [{ type: core.ContentChild, args: ['graniteTableCellTemplate', { static: false },] }]
2944
+ };
2945
+
2946
+ var TableConstants = /** @class */ (function () {
2947
+ function TableConstants() {
2948
+ }
2949
+ return TableConstants;
2950
+ }());
2951
+ TableConstants.CELL_ID_PREFIX = 'granite-cell';
2952
+
2953
+ var GraniteTableComponent = /** @class */ (function () {
2954
+ function GraniteTableComponent() {
2955
+ this.ariaLabel = null;
2956
+ this.cellIdPrefix = TableConstants.CELL_ID_PREFIX;
2957
+ }
2958
+ GraniteTableComponent.prototype.ngAfterContentInit = function () {
2959
+ this.columns = this.tableColumnsComponent.toArray();
2960
+ this.renderedColumns = this.columns.map(function (column) { return column.name; });
2961
+ };
2962
+ return GraniteTableComponent;
2963
+ }());
2964
+ GraniteTableComponent.decorators = [
2965
+ { type: core.Component, args: [{
2966
+ selector: 'granite-table',
2967
+ template: "<cdk-table\n [dataSource]=\"dataSource\"\n multiTemplateDataRows\n [trackBy]=\"trackBy\"\n [attr.aria-label]=\"ariaLabel\"\n>\n <ng-container\n *ngFor=\"let column of columns; index as columnI\"\n [cdkColumnDef]=\"column.name\"\n >\n <!-- Cell Header -->\n <cdk-header-cell *cdkHeaderCellDef graniteTableHeaderCell>\n {{ (column.title ? column.title : column.name) | graniteTitle }}\n </cdk-header-cell>\n\n <!-- Cell Data -->\n <cdk-cell\n graniteTableDataCell\n *cdkCellDef=\"let element; let rowI = dataIndex\"\n [id]=\"cellIdPrefix + '-' + rowI + '-' + columnI\"\n [value]=\"element[column.name]\"\n [rowIndex]=\"rowI\"\n [columnIndex]=\"columnI\"\n [staticType]=\"column.staticType\"\n [column]=\"column\"\n [tableCellTemplateRef]=\"column.tableCellTemplateRef\"\n ></cdk-cell>\n </ng-container>\n\n <cdk-header-row *cdkHeaderRowDef=\"renderedColumns\"></cdk-header-row>\n <cdk-row\n *cdkRowDef=\"let row; let rowI = dataIndex; columns: renderedColumns\"\n [attr.aria-rowindex]=\"rowI + 1\"\n ></cdk-row>\n</cdk-table>\n",
2968
+ changeDetection: core.ChangeDetectionStrategy.OnPush,
2969
+ styles: [":host{box-sizing:border-box}:host *,:host :after,:host :before{box-sizing:inherit}cdk-table{flex-direction:column;background-color:var(--granite-color-background-variant)}cdk-header-row,cdk-row,cdk-table{display:flex}cdk-cell,cdk-header-cell{flex:1}cdk-cell{height:calc(var(--granite-spacing-m) * 2.5);border-top:1px solid var(--granite-color-border-soft)}"]
2970
+ },] }
2971
+ ];
2972
+ GraniteTableComponent.propDecorators = {
2973
+ dataSource: [{ type: core.Input }],
2974
+ trackBy: [{ type: core.Input }],
2975
+ ariaLabel: [{ type: core.Input, args: ['aria-label',] }],
2976
+ tableColumnsComponent: [{ type: core.ContentChildren, args: [GraniteTableColumnDirective,] }]
2977
+ };
2978
+
2979
+ var GraniteCell = /** @class */ (function () {
2980
+ function GraniteCell() {
2981
+ }
2982
+ return GraniteCell;
2983
+ }());
2984
+ GraniteCell.decorators = [
2985
+ { type: core.Directive }
2986
+ ];
2987
+ GraniteCell.propDecorators = {
2988
+ id: [{ type: core.Input }],
2989
+ column: [{ type: core.Input }]
2990
+ };
2991
+
2992
+ var GraniteTableDataCellComponent = /** @class */ (function (_super) {
2993
+ __extends(GraniteTableDataCellComponent, _super);
2994
+ function GraniteTableDataCellComponent() {
2995
+ return _super !== null && _super.apply(this, arguments) || this;
2996
+ }
2997
+ return GraniteTableDataCellComponent;
2998
+ }(GraniteCell));
2999
+ GraniteTableDataCellComponent.decorators = [
3000
+ { type: core.Component, args: [{
3001
+ // eslint-disable-next-line @angular-eslint/component-selector
3002
+ selector: 'cdk-cell[graniteTableDataCell]',
3003
+ template: "<span class=\"granite-table-data-cell-static-container\">\n <ng-template\n [ngTemplateOutlet]=\"tableCellTemplateRef || defaultTableCellTemplate\"\n [ngTemplateOutletContext]=\"{\n data: value,\n rowIndex: rowIndex,\n columnIndex: columnIndex,\n column: column\n }\"\n ></ng-template>\n</span>\n\n<ng-template #defaultTableCellTemplate>\n <ng-container [ngSwitch]=\"staticType\">\n <ng-container *ngSwitchCase=\"'badge'\">\n <granite-badge>{{ value }}</granite-badge>\n </ng-container>\n <ng-container *ngSwitchDefault>{{ value }}</ng-container>\n </ng-container>\n</ng-template>\n",
3004
+ styles: [".granite-table-data-cell-static-container{padding:var(--granite-spacing-s);height:var(--granite-spacing-l);font-size:var(--granite-font-size-body-small);line-height:var(--granite-line-height-regular);display:flex;justify-content:center;align-items:center;color:var(--granite-color-text)}"]
3005
+ },] }
3006
+ ];
3007
+ GraniteTableDataCellComponent.propDecorators = {
3008
+ value: [{ type: core.Input }],
3009
+ staticType: [{ type: core.Input }],
3010
+ rowIndex: [{ type: core.Input }],
3011
+ columnIndex: [{ type: core.Input }],
3012
+ tableCellTemplateRef: [{ type: core.Input }]
3013
+ };
3014
+
3015
+ var GraniteTableHeaderCellComponent = /** @class */ (function (_super) {
3016
+ __extends(GraniteTableHeaderCellComponent, _super);
3017
+ function GraniteTableHeaderCellComponent() {
3018
+ return _super !== null && _super.apply(this, arguments) || this;
3019
+ }
3020
+ return GraniteTableHeaderCellComponent;
3021
+ }(GraniteCell));
3022
+ GraniteTableHeaderCellComponent.decorators = [
3023
+ { type: core.Component, args: [{
3024
+ // eslint-disable-next-line @angular-eslint/component-selector
3025
+ selector: 'cdk-header-cell[graniteTableHeaderCell]',
3026
+ template: "<span class=\"granite-table-header-cell-title\">\n <ng-content></ng-content>\n</span>\n",
3027
+ styles: [".granite-table-header-cell-title{padding:var(--granite-spacing-s);height:var(--granite-spacing-l);font-size:var(--granite-font-size-micro);line-height:var(--granite-line-height-flowing);letter-spacing:.015em;font-weight:400;display:flex;justify-content:center;align-items:center;color:var(--granite-color-text-weak)}"]
3028
+ },] }
3029
+ ];
3030
+
3031
+ var GraniteTitlePipe = /** @class */ (function () {
3032
+ function GraniteTitlePipe() {
3033
+ }
3034
+ GraniteTitlePipe.prototype.transform = function (value) {
3035
+ var words = value.split(/(?=[A-Z ])/);
3036
+ return words
3037
+ .map(function (word) { return word.trim(); })
3038
+ .filter(Boolean)
3039
+ .map(function (word) { return word.charAt(0).toLocaleUpperCase() + word.slice(1); })
3040
+ .join(' ');
3041
+ };
3042
+ return GraniteTitlePipe;
3043
+ }());
3044
+ GraniteTitlePipe.decorators = [
3045
+ { type: core.Pipe, args: [{
3046
+ name: 'graniteTitle',
3047
+ },] }
3048
+ ];
3049
+
3050
+ var PurePipesModule = /** @class */ (function () {
3051
+ function PurePipesModule() {
3052
+ }
3053
+ return PurePipesModule;
3054
+ }());
3055
+ PurePipesModule.decorators = [
3056
+ { type: core.NgModule, args: [{
3057
+ exports: [GraniteTitlePipe],
3058
+ declarations: [GraniteTitlePipe],
3059
+ },] }
3060
+ ];
3061
+
3062
+ var GraniteTableModule = /** @class */ (function () {
3063
+ function GraniteTableModule() {
3064
+ }
3065
+ return GraniteTableModule;
3066
+ }());
3067
+ GraniteTableModule.decorators = [
3068
+ { type: core.NgModule, args: [{
3069
+ declarations: [
3070
+ GraniteTableComponent,
3071
+ GraniteTableDataCellComponent,
3072
+ GraniteTableHeaderCellComponent,
3073
+ GraniteTableColumnDirective,
3074
+ ],
3075
+ imports: [common.CommonModule, table.CdkTableModule, GraniteBadgeModule, PurePipesModule],
3076
+ exports: [GraniteTableComponent, GraniteTableColumnDirective],
3077
+ },] }
3078
+ ];
3079
+
3080
+ var GRANITE_INPUT_INCLUDES = ['text', 'number', 'password', 'textarea'];
3081
+ var GraniteInputFieldComponent = /** @class */ (function () {
3082
+ function GraniteInputFieldComponent(_focusMonitor) {
3083
+ this._focusMonitor = _focusMonitor;
3084
+ this.id = null;
3085
+ this.name = null;
3086
+ this.type = 'text';
3087
+ this.value = '';
3088
+ this.required = false;
3089
+ this.readonly = false;
3090
+ this.invalid = false;
3091
+ this.disabled = false;
3092
+ this.placeholder = '';
3093
+ this.maxlength = 255;
3094
+ this.countcharacters = false;
3095
+ this.ariaLabel = null;
3096
+ this.ariaLabelledby = null;
3097
+ this.valueChange = new core.EventEmitter();
3098
+ this._supported = true;
3099
+ this._empty = false;
3100
+ this._passwordFieldIcon = 'view';
3101
+ this._passwordField = false;
3102
+ this._passwordToggled = false;
3103
+ this._currentCharCount = 0;
3104
+ }
3105
+ GraniteInputFieldComponent.prototype.ngOnInit = function () {
3106
+ this._validateType();
3107
+ this._passwordField = this.type == 'password';
3108
+ this._empty = this.value == null || this.value === '';
3109
+ };
3110
+ GraniteInputFieldComponent.prototype.ngOnChanges = function (changes) {
3111
+ if (changes.required) {
3112
+ this.required = coercion.coerceBooleanProperty(changes.required.currentValue);
3113
+ }
3114
+ if (changes.readonly) {
3115
+ this.readonly = coercion.coerceBooleanProperty(changes.readonly.currentValue);
3116
+ }
3117
+ if (changes.invalid) {
3118
+ this.invalid = coercion.coerceBooleanProperty(changes.invalid.currentValue);
3119
+ }
3120
+ if (changes.disabled) {
3121
+ this.disabled = coercion.coerceBooleanProperty(changes.disabled.currentValue);
3122
+ }
3123
+ if (changes.countcharacters) {
3124
+ this.countcharacters = coercion.coerceBooleanProperty(changes.countcharacters.currentValue);
3125
+ }
3126
+ if (changes.value) {
3127
+ this._empty = this.value == null || this.value === '';
3128
+ }
3129
+ if (changes.type) {
3130
+ this._validateType();
3131
+ }
3132
+ };
3133
+ GraniteInputFieldComponent.prototype.focus = function (origin, options) {
3134
+ if (origin === void 0) { origin = 'program'; }
3135
+ if (this.type === 'text') {
3136
+ this._focusMonitor.focusVia(this._getInputElement(), origin, options);
3137
+ }
3138
+ else if (this.type === 'textarea') {
3139
+ this._focusMonitor.focusVia(this._getTextareaElement(), origin, options);
3140
+ }
3141
+ };
3142
+ GraniteInputFieldComponent.prototype._togglePassword = function () {
3143
+ if (this._passwordToggled) {
3144
+ this._passwordToggled = false;
3145
+ this.type = 'password';
3146
+ this._passwordFieldIcon = 'view';
3147
+ }
3148
+ else {
3149
+ this._passwordToggled = true;
3150
+ this.type = 'text';
3151
+ this._passwordFieldIcon = 'view-disabled';
3152
+ }
3153
+ };
3154
+ GraniteInputFieldComponent.prototype._onKeyUp = function (event) {
3155
+ var inputEvent = event.target;
3156
+ this._applyCharacterCount(inputEvent.value);
3157
+ this._empty = inputEvent.value == null || inputEvent.value === '';
3158
+ this.valueChange.emit(inputEvent.value);
3159
+ };
3160
+ GraniteInputFieldComponent.prototype._onInput = function (event) {
3161
+ var inputEvent = event.target;
3162
+ this._empty = inputEvent.value == null || inputEvent.value === '';
3163
+ this.valueChange.emit(inputEvent.value);
3164
+ };
3165
+ GraniteInputFieldComponent.prototype._validateType = function () {
3166
+ if (GRANITE_INPUT_INCLUDES.indexOf(this.type) < 0) {
3167
+ this._supported = false;
3168
+ throw Error("Input type \"" + this.type + "\" isn't supported by graniteInputField.");
3169
+ }
3170
+ };
3171
+ GraniteInputFieldComponent.prototype._applyCharacterCount = function (inputString) {
3172
+ if (this.countcharacters) {
3173
+ this._currentCharCount = inputString.length;
3174
+ if (this._currentCharCount > this.maxlength) {
3175
+ inputString = inputString.slice(0, this.maxlength);
3176
+ this._currentCharCount = this.maxlength;
3177
+ }
3178
+ }
3179
+ };
3180
+ GraniteInputFieldComponent.prototype._getInputElement = function () {
3181
+ return this._inputElement.nativeElement;
3182
+ };
3183
+ GraniteInputFieldComponent.prototype._getTextareaElement = function () {
3184
+ return this._textareaElement.nativeElement;
3185
+ };
3186
+ return GraniteInputFieldComponent;
3187
+ }());
3188
+ GraniteInputFieldComponent.decorators = [
3189
+ { type: core.Component, args: [{
3190
+ selector: 'granite-input-field',
3191
+ exportAs: 'graniteInputField',
3192
+ template: "<div\n *ngIf=\"_supported\"\n class=\"granite-input-container\"\n [class.granite-input-disabled]=\"disabled\"\n [class.granite-input-readonly]=\"readonly\"\n [class.granite-input-disabled]=\"disabled\"\n [class.granite-input-readonly]=\"readonly\"\n>\n <div\n class=\"granite-input-top-row\"\n [class.granite-input-required]=\"required\"\n [class.granite-input-empty]=\"_empty\"\n [class.granite-input-disabled]=\"disabled\"\n [class.granite-input-readonly]=\"readonly\"\n >\n <div\n *ngIf=\"prefixicon\"\n class=\"granite-input-prepend\"\n [class.granite-input-required]=\"required\"\n [class.granite-input-empty]=\"_empty\"\n >\n <granite-icon class=\"granite-input-prepend-icon\">\n {{ prefixicon }}\n </granite-icon>\n </div>\n\n <ng-container\n *ngIf=\"type != 'textarea'; then inputElement; else textareaElement\"\n ></ng-container>\n\n <ng-template #inputElement>\n <input\n #input\n [id]=\"id\"\n class=\"granite-input-base\"\n [class.granite-input-invalid]=\"invalid\"\n [class.granite-input-empty]=\"_empty\"\n [name]=\"name\"\n [attr.type]=\"type\"\n [required]=\"required\"\n [readonly]=\"readonly\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n [attr.maxlength]=\"maxlength\"\n [value]=\"value\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n [attr.aria-invalid]=\"invalid\"\n (keyup)=\"_onKeyUp($event)\"\n (input)=\"_onInput($event)\"\n />\n </ng-template>\n\n <button\n *ngIf=\"_passwordField\"\n class=\"granite-input-append\"\n [class.granite-input-required]=\"required\"\n [class.granite-input-empty]=\"_empty\"\n (click)=\"_togglePassword()\"\n >\n <granite-icon class=\"granite-input-password-toggle-icon\">\n {{ _passwordFieldIcon }}\n </granite-icon>\n </button>\n\n <ng-template #textareaElement>\n <textarea\n #textarea\n [id]=\"id\"\n class=\"granite-input-base granite-text-area\"\n [class.granite-input-invalid]=\"invalid\"\n [class.granite-input-empty]=\"_empty\"\n rows=\"1\"\n [name]=\"name\"\n [attr.type]=\"type\"\n [required]=\"required\"\n [readonly]=\"readonly\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n [attr.maxlength]=\"maxlength\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n [attr.aria-required]=\"required\"\n [attr.aria-invalid]=\"invalid\"\n (keyup)=\"_onKeyUp($event)\"\n (input)=\"_onInput($event)\"\n ></textarea>\n </ng-template>\n\n <div\n class=\"granite-input-hover-bar\"\n [class.granite-input-invalid]=\"invalid\"\n [class.granite-input-empty]=\"_empty\"\n ></div>\n </div>\n\n <div *ngIf=\"countcharacters\" class=\"granite-input-bottom-row\">\n <div class=\"granite-input-char-count\">\n {{ _currentCharCount }}/{{ maxlength }}\n </div>\n </div>\n</div>\n",
3193
+ host: {
3194
+ class: 'granite-input-field',
3195
+ },
3196
+ changeDetection: core.ChangeDetectionStrategy.OnPush,
3197
+ styles: [":host{transition:all .2s ease-out;width:calc(var(--granite-spacing-3-xl) * 3.625);height:var(--granite-spacing-xl);box-sizing:border-box}:host *,:host :after,:host :before{box-sizing:inherit}.granite-input-container{height:inherit;width:inherit;font-size:var(--granite-font-size-body-small)}.granite-input-container .granite-input-top-row{display:inline-flex;width:inherit;height:inherit;position:relative;background:var(--granite-color-background)}.granite-input-container .granite-input-top-row:hover .granite-input-hover-bar{height:calc(var(--granite-spacing-xs)/2)}.granite-input-container .granite-input-top-row:hover .granite-input-hover-bar.granite-input-invalid.granite-input-empty{background-color:var(--granite-color-focus)}.granite-input-container .granite-input-top-row .granite-text-area{min-width:calc(var(--granite-spacing-3-xl) * 3.625);min-height:var(--granite-spacing-xl)}.granite-input-container .granite-input-top-row.granite-input-disabled,.granite-input-container .granite-input-top-row.granite-input-readonly{background-color:transparent;box-shadow:none}.granite-input-container .granite-input-top-row.granite-input-disabled .granite-input-hover-bar,.granite-input-container .granite-input-top-row.granite-input-readonly .granite-input-hover-bar{background-color:transparent}.granite-input-container .granite-input-top-row .granite-input-base{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;border:none;background-color:var(--granite-color-background-input);padding:var(--granite-spacing-s);width:inherit;height:inherit;color:var(--granite-color-text);font:inherit;font-weight:var(--granite-font-weight-regular);line-height:100%}.granite-input-container .granite-input-top-row .granite-input-base:required.granite-input-empty{background-color:var(--granite-color-background-failure)}.granite-input-container .granite-input-top-row .granite-input-base:required::-moz-placeholder{color:var(--granite-color-text-weak)}.granite-input-container .granite-input-top-row .granite-input-base:required:-ms-input-placeholder{color:var(--granite-color-text-weak)}.granite-input-container .granite-input-top-row .granite-input-base:required::placeholder{color:var(--granite-color-text-weak)}.granite-input-container .granite-input-top-row .granite-input-base:-moz-read-only{background-color:transparent}.granite-input-container .granite-input-top-row .granite-input-base:read-only{background-color:transparent}.granite-input-container .granite-input-top-row .granite-input-base:disabled{opacity:.3}.granite-input-container .granite-input-top-row .granite-input-base::-moz-placeholder{color:var(--granite-color-text-hint)}.granite-input-container .granite-input-top-row .granite-input-base:-ms-input-placeholder{color:var(--granite-color-text-hint)}.granite-input-container .granite-input-top-row .granite-input-base::placeholder{color:var(--granite-color-text-hint)}.granite-input-container .granite-input-top-row .granite-input-base:hover::-moz-placeholder{color:var(--granite-color-text)}.granite-input-container .granite-input-top-row .granite-input-base:hover:-ms-input-placeholder{color:var(--granite-color-text)}.granite-input-container .granite-input-top-row .granite-input-base:hover::placeholder{color:var(--granite-color-text)}.granite-input-container .granite-input-top-row .granite-input-base:focus{box-shadow:inset 0 calc(var(--granite-spacing-xs)/2) var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2) 0 var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2 * -1) 0 var(--granite-color-focus),inset 0 calc(var(--granite-spacing-xs)/2 * -1) var(--granite-color-focus)}.granite-input-container .granite-input-top-row .granite-input-base:focus.granite-input-invalid{box-shadow:inset 0 calc(var(--granite-spacing-xs)/2 * -1) var(--granite-color-signal-failure),inset 0 calc(var(--granite-spacing-xs)/2) var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2) 0 var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2 * -1) 0 var(--granite-color-focus)}.granite-input-container .granite-input-top-row .granite-input-base:focus::-moz-placeholder{color:transparent}.granite-input-container .granite-input-top-row .granite-input-base:focus:-ms-input-placeholder{color:transparent}.granite-input-container .granite-input-top-row .granite-input-base:focus::placeholder{color:transparent}.granite-input-container .granite-input-top-row .granite-input-hover-bar{height:calc(var(--granite-spacing-xs)/4);background-color:var(--granite-color-border-hard);position:absolute;width:inherit;bottom:0}.granite-input-container .granite-input-top-row .granite-input-hover-bar.granite-input-invalid{background-color:var(--granite-color-signal-failure)}.granite-input-container .granite-input-top-row:focus-within .granite-input-hover-bar{background-color:transparent}.granite-input-container .granite-input-prepend{display:flex;align-items:center;padding:0 var(--granite-spacing-s);background:var(--granite-color-background-input)}.granite-input-container .granite-input-prepend .granite-input-prepend-icon{width:var(--granite-spacing-m);height:var(--granite-spacing-m);color:var(--granite-color-text);box-shadow:none}.granite-input-container .granite-input-prepend.granite-input-required.granite-input-empty{background-color:var(--granite-color-background-failure)}.granite-input-container .granite-input-append{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;border:none;background-color:var(--granite-color-background-input);position:relative}.granite-input-container .granite-input-append:focus{box-shadow:inset 0 calc(var(--granite-spacing-xs)/2) var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2) 0 var(--granite-color-focus),inset calc(var(--granite-spacing-xs)/2 * -1) 0 var(--granite-color-focus),inset 0 calc(var(--granite-spacing-xs)/2 * -1) var(--granite-color-focus)}.granite-input-container .granite-input-append .granite-input-password-toggle-icon{width:-webkit-max-content;width:-moz-max-content;width:max-content;height:-webkit-max-content;height:-moz-max-content;height:max-content;color:var(--granite-color-text);box-shadow:none}.granite-input-container .granite-input-append.granite-input-required.granite-input-empty{background-color:var(--granite-color-background-failure)}.granite-input-container .granite-input-bottom-row{box-shadow:none}.granite-input-container .granite-input-char-count{background:var(--granite-color-background-warning);border-radius:0 0 var(--granite-spacing-xs) var(--granite-spacing-xs);padding:var(--granite-spacing-s);background-size:contain;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;box-shadow:none}.granite-input-container.granite-input-disabled,.granite-input-container.granite-input-readonly{background-color:transparent}"]
3198
+ },] }
3199
+ ];
3200
+ GraniteInputFieldComponent.ctorParameters = function () { return [
3201
+ { type: a11y.FocusMonitor }
3202
+ ]; };
3203
+ GraniteInputFieldComponent.propDecorators = {
3204
+ id: [{ type: core.Input }],
3205
+ name: [{ type: core.Input }],
3206
+ type: [{ type: core.Input }],
3207
+ value: [{ type: core.Input }],
3208
+ required: [{ type: core.Input }],
3209
+ readonly: [{ type: core.Input }],
3210
+ invalid: [{ type: core.Input }],
3211
+ disabled: [{ type: core.Input }],
3212
+ placeholder: [{ type: core.Input }],
3213
+ prefixicon: [{ type: core.Input }],
3214
+ maxlength: [{ type: core.Input }],
3215
+ countcharacters: [{ type: core.Input }],
3216
+ ariaLabel: [{ type: core.Input, args: ['aria-label',] }],
3217
+ ariaLabelledby: [{ type: core.Input, args: ['aria-labelledby',] }],
3218
+ valueChange: [{ type: core.Output }],
3219
+ _inputElement: [{ type: core.ViewChild, args: ['input',] }],
3220
+ _textareaElement: [{ type: core.ViewChild, args: ['textarea',] }]
3221
+ };
3222
+
3223
+ var GraniteInputFieldModule = /** @class */ (function () {
3224
+ function GraniteInputFieldModule() {
3225
+ }
3226
+ return GraniteInputFieldModule;
3227
+ }());
3228
+ GraniteInputFieldModule.decorators = [
3229
+ { type: core.NgModule, args: [{
3230
+ imports: [common.CommonModule, GraniteIconModule, GraniteButtonModule],
3231
+ declarations: [GraniteInputFieldComponent],
3232
+ exports: [GraniteInputFieldComponent],
3233
+ },] }
3234
+ ];
3235
+
2808
3236
  var ɵ0 = deviceDesktop.output;
2809
3237
  /**
2810
3238
  * Directive used to tell components and their sub components that client output
@@ -2946,6 +3374,8 @@
2946
3374
  exports.GraniteGridModule = GraniteGridModule;
2947
3375
  exports.GraniteIconComponent = GraniteIconComponent;
2948
3376
  exports.GraniteIconModule = GraniteIconModule;
3377
+ exports.GraniteInputFieldComponent = GraniteInputFieldComponent;
3378
+ exports.GraniteInputFieldModule = GraniteInputFieldModule;
2949
3379
  exports.GraniteMenuComponent = GraniteMenuComponent;
2950
3380
  exports.GraniteMenuHarness = GraniteMenuHarness;
2951
3381
  exports.GraniteMenuItemComponent = GraniteMenuItemComponent;
@@ -2954,6 +3384,11 @@
2954
3384
  exports.GraniteMenuTouchCloseComponent = GraniteMenuTouchCloseComponent;
2955
3385
  exports.GraniteMenuTouchTitleItemComponent = GraniteMenuTouchTitleItemComponent;
2956
3386
  exports.GraniteMenuTriggerForDirective = GraniteMenuTriggerForDirective;
3387
+ exports.GraniteRadioButtonComponent = GraniteRadioButtonComponent;
3388
+ exports.GraniteRadioButtonModule = GraniteRadioButtonModule;
3389
+ exports.GraniteTableColumnDirective = GraniteTableColumnDirective;
3390
+ exports.GraniteTableComponent = GraniteTableComponent;
3391
+ exports.GraniteTableModule = GraniteTableModule;
2957
3392
  exports.GraniteToggleSwitchComponent = GraniteToggleSwitchComponent;
2958
3393
  exports.GraniteToggleSwitchModule = GraniteToggleSwitchModule;
2959
3394
  exports._MenuBaseComponent = _MenuBaseComponent;
@@ -2966,6 +3401,11 @@
2966
3401
  exports.ɵa = GRANITE_MENU_PANEL;
2967
3402
  exports.ɵc = getEaseOutSteep;
2968
3403
  exports.ɵd = getEaseLinear;
3404
+ exports.ɵe = GraniteTableDataCellComponent;
3405
+ exports.ɵf = GraniteCell;
3406
+ exports.ɵg = GraniteTableHeaderCellComponent;
3407
+ exports.ɵh = PurePipesModule;
3408
+ exports.ɵi = GraniteTitlePipe;
2969
3409
 
2970
3410
  Object.defineProperty(exports, '__esModule', { value: true });
2971
3411