@ajsf/material 22.3.3 → 22.4.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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @ajsf/material
2
2
 
3
+ [![Repository coverage, SonarQube Cloud](https://sonarcloud.io/api/project_badges/measure?project=hamzahamidi_ajsf&metric=coverage)](https://sonarcloud.io/summary/new_code?id=hamzahamidi_ajsf)
4
+
3
5
  Angular Material framework for [AJSF](https://github.com/hamzahamidi/ajsf#readme), which builds Angular forms from JSON Schema.
4
6
 
5
7
  ## Getting started
@@ -63,10 +63,10 @@ class FlexLayoutRootComponent {
63
63
  baseline: 'baseline', stretch: 'stretch',
64
64
  }; }
65
65
  getFlexBasis(node) {
66
- return (node?.options || {}).fxFlex || this.getFlexAttribute(node, 'flex-basis');
66
+ return node?.options?.fxFlex || this.getFlexAttribute(node, 'flex-basis');
67
67
  }
68
68
  getMaxWidth(node) {
69
- const fxFlex = (node?.options || {}).fxFlex;
69
+ const fxFlex = node?.options?.fxFlex;
70
70
  return fxFlex && `${fxFlex}`.trim().endsWith('%') ? fxFlex : null;
71
71
  }
72
72
  getAlignSelf(node) {
@@ -79,14 +79,14 @@ class FlexLayoutRootComponent {
79
79
  return options.order || options.fxFlexOrder || null;
80
80
  }
81
81
  getOffset(node) {
82
- return (node?.options || {}).fxFlexOffset || null;
82
+ return node?.options?.fxFlexOffset || null;
83
83
  }
84
84
  // Set attributes for flexbox child
85
85
  // (container attributes are set in flex-layout-section.component)
86
86
  getFlexAttribute(node, attribute) {
87
87
  const index = ['flex-grow', 'flex-shrink', 'flex-basis'].indexOf(attribute);
88
- return ((node.options || {}).flex || '').split(/\s+/)[index] ||
89
- (node.options || {})[attribute] || ['1', '1', 'auto'][index];
88
+ return (node.options?.flex || '').split(/\s+/)[index] ||
89
+ node.options?.[attribute] || ['1', '1', 'auto'][index];
90
90
  }
91
91
  showWidget(layoutNode) {
92
92
  return this.jsf.evaluateCondition(layoutNode, this.dataIndex);
@@ -201,7 +201,7 @@ class FlexLayoutSectionComponent {
201
201
  'space-evenly': 'space-evenly', stretch: 'stretch', baseline: 'baseline',
202
202
  }; }
203
203
  fxAlign(position) {
204
- const value = `${(this.options || {}).fxLayoutAlign || ''}`.trim().split(/\s+/)[position];
204
+ const value = `${this.options?.fxLayoutAlign || ''}`.trim().split(/\s+/)[position];
205
205
  return FlexLayoutSectionComponent.ALIGN[value] || null;
206
206
  }
207
207
  getJustifyContent() {
@@ -223,10 +223,11 @@ class FlexLayoutSectionComponent {
223
223
  case 'display':
224
224
  return flexActive ? 'flex' : 'initial';
225
225
  case 'flex-direction':
226
- case 'flex-wrap':
226
+ case 'flex-wrap': {
227
227
  const index = ['flex-direction', 'flex-wrap'].indexOf(attribute);
228
228
  return (this.options['flex-flow'] || '').split(/\s+/)[index] ||
229
229
  this.options[attribute] || ['column', 'nowrap'][index];
230
+ }
230
231
  case 'justify-content':
231
232
  case 'align-items':
232
233
  case 'align-content':
@@ -574,7 +575,7 @@ class MaterialAddReferenceComponent {
574
575
  }
575
576
  get showAddButton() {
576
577
  return !this.layoutNode.arrayItem ||
577
- this.layoutIndex[this.layoutIndex.length - 1] < this.options.maxItems;
578
+ this.layoutIndex.at(-1) < this.options.maxItems;
578
579
  }
579
580
  addItem(event) {
580
581
  event.preventDefault();
@@ -1431,11 +1432,15 @@ class MaterialDesignFrameworkComponent {
1431
1432
  return false;
1432
1433
  }
1433
1434
  // If array length <= minItems, don't allow removing any items
1434
- return this.parentArray.items.length - 1 <= this.parentArray.options.minItems ? false :
1435
- // For removable list items, allow removing any item
1436
- this.layoutNode.arrayItemType === 'list' ? true :
1437
- // For removable tuple items, only allow removing last item in list
1438
- this.layoutIndex[this.layoutIndex.length - 1] === this.parentArray.items.length - 2;
1435
+ if (this.parentArray.items.length - 1 <= this.parentArray.options.minItems) {
1436
+ return false;
1437
+ }
1438
+ // For removable list items, allow removing any item
1439
+ if (this.layoutNode.arrayItemType === 'list') {
1440
+ return true;
1441
+ }
1442
+ // For removable tuple items, only allow removing last item in list
1443
+ return this.layoutIndex.at(-1) === this.parentArray.items.length - 2;
1439
1444
  }
1440
1445
  ngOnInit() {
1441
1446
  this.initializeFramework();
@@ -1486,17 +1491,17 @@ class MaterialDesignFrameworkComponent {
1486
1491
  }
1487
1492
  }
1488
1493
  updateTitle() {
1489
- this.widgetLayoutNode.options.title = this.jsf.parseText(this.dynamicTitle, this.jsf.getFormControlValue(this), this.jsf.getFormControlGroup(this).value, this.dataIndex[this.dataIndex.length - 1]);
1494
+ this.widgetLayoutNode.options.title = this.jsf.parseText(this.dynamicTitle, this.jsf.getFormControlValue(this), this.jsf.getFormControlGroup(this).value, this.dataIndex.at(-1));
1490
1495
  }
1491
1496
  removeItem() {
1492
1497
  this.jsf.removeItem(this);
1493
1498
  }
1494
1499
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MaterialDesignFrameworkComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.JsonSchemaFormService }], target: i0.ɵɵFactoryTarget.Component }); }
1495
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: MaterialDesignFrameworkComponent, isStandalone: false, selector: "material-design-framework", inputs: { layoutNode: "layoutNode", layoutIndex: "layoutIndex", dataIndex: "dataIndex" }, usesOnChanges: true, ngImport: i0, template: "<div\n [class.array-item]=\"widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref'\"\n [orderable]=\"isOrderable\"\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\">\n @if (showRemoveButton) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"18\" width=\"18\" viewBox=\"0 0 24 24\"\n class=\"close-button\"\n (click)=\"removeItem()\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z\"/>\n </svg>\n }\n <select-widget-widget\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\"></select-widget-widget>\n</div>\n@if (widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref') {\n <div class=\"spacer\"></div>\n}\n", styles: [".array-item{border-radius:2px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;padding:6px;position:relative;transition:all .28s cubic-bezier(.4,0,.2,1)}.close-button{cursor:pointer;position:absolute;top:6px;right:6px;fill:#0006;visibility:hidden;z-index:500}.close-button:hover{fill:#000c}.array-item:hover>.close-button{visibility:visible}.spacer{margin:6px 0}[draggable=true]:hover{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;cursor:move;z-index:10}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"], dependencies: [{ kind: "component", type: i1.SelectWidgetComponent, selector: "select-widget-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }, { kind: "directive", type: i1.OrderableDirective, selector: "[orderable]", inputs: ["orderable", "layoutNode", "layoutIndex", "dataIndex"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
1500
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: MaterialDesignFrameworkComponent, isStandalone: false, selector: "material-design-framework", inputs: { layoutNode: "layoutNode", layoutIndex: "layoutIndex", dataIndex: "dataIndex" }, usesOnChanges: true, ngImport: i0, template: "<div\n [class.array-item]=\"widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref'\"\n [orderable]=\"isOrderable\"\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\">\n @if (showRemoveButton) {\n <button\n type=\"button\"\n class=\"close-button\"\n aria-label=\"Remove item\"\n (click)=\"removeItem()\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"18\" width=\"18\" viewBox=\"0 0 24 24\"\n aria-hidden=\"true\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z\"/>\n </svg>\n </button>\n }\n <select-widget-widget\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\"></select-widget-widget>\n</div>\n@if (widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref') {\n <div class=\"spacer\"></div>\n}\n", styles: [".array-item{border-radius:2px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;padding:6px;position:relative;transition:box-shadow .28s cubic-bezier(.4,0,.2,1)}.close-button{background:transparent;border:0;cursor:pointer;padding:0;position:absolute;top:6px;right:6px;fill:#0006;opacity:0;pointer-events:none;z-index:500}.close-button:hover{fill:#000c}.array-item:hover>.close-button,.array-item:focus-within>.close-button{opacity:1;pointer-events:auto}.spacer{margin:6px 0}[draggable=true]:hover{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;cursor:move;z-index:10}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"], dependencies: [{ kind: "component", type: i1.SelectWidgetComponent, selector: "select-widget-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }, { kind: "directive", type: i1.OrderableDirective, selector: "[orderable]", inputs: ["orderable", "layoutNode", "layoutIndex", "dataIndex"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
1496
1501
  }
1497
1502
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MaterialDesignFrameworkComponent, decorators: [{
1498
1503
  type: Component,
1499
- args: [{ selector: 'material-design-framework', changeDetection: ChangeDetectionStrategy.Eager, standalone: false, template: "<div\n [class.array-item]=\"widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref'\"\n [orderable]=\"isOrderable\"\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\">\n @if (showRemoveButton) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"18\" width=\"18\" viewBox=\"0 0 24 24\"\n class=\"close-button\"\n (click)=\"removeItem()\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z\"/>\n </svg>\n }\n <select-widget-widget\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\"></select-widget-widget>\n</div>\n@if (widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref') {\n <div class=\"spacer\"></div>\n}\n", styles: [".array-item{border-radius:2px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;padding:6px;position:relative;transition:all .28s cubic-bezier(.4,0,.2,1)}.close-button{cursor:pointer;position:absolute;top:6px;right:6px;fill:#0006;visibility:hidden;z-index:500}.close-button:hover{fill:#000c}.array-item:hover>.close-button{visibility:visible}.spacer{margin:6px 0}[draggable=true]:hover{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;cursor:move;z-index:10}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"] }]
1504
+ args: [{ selector: 'material-design-framework', changeDetection: ChangeDetectionStrategy.Eager, standalone: false, template: "<div\n [class.array-item]=\"widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref'\"\n [orderable]=\"isOrderable\"\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\">\n @if (showRemoveButton) {\n <button\n type=\"button\"\n class=\"close-button\"\n aria-label=\"Remove item\"\n (click)=\"removeItem()\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"18\" width=\"18\" viewBox=\"0 0 24 24\"\n aria-hidden=\"true\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z\"/>\n </svg>\n </button>\n }\n <select-widget-widget\n [dataIndex]=\"dataIndex\"\n [layoutIndex]=\"layoutIndex\"\n [layoutNode]=\"widgetLayoutNode\"></select-widget-widget>\n</div>\n@if (widgetLayoutNode?.arrayItem && widgetLayoutNode?.type !== '$ref') {\n <div class=\"spacer\"></div>\n}\n", styles: [".array-item{border-radius:2px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;padding:6px;position:relative;transition:box-shadow .28s cubic-bezier(.4,0,.2,1)}.close-button{background:transparent;border:0;cursor:pointer;padding:0;position:absolute;top:6px;right:6px;fill:#0006;opacity:0;pointer-events:none;z-index:500}.close-button:hover{fill:#000c}.array-item:hover>.close-button,.array-item:focus-within>.close-button{opacity:1;pointer-events:auto}.spacer{margin:6px 0}[draggable=true]:hover{box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;cursor:move;z-index:10}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"] }]
1500
1505
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.JsonSchemaFormService }], propDecorators: { layoutNode: [{
1501
1506
  type: Input
1502
1507
  }], layoutIndex: [{
@@ -1968,7 +1973,13 @@ class MaterialOneOfComponent {
1968
1973
  const type = this.layoutNode.type;
1969
1974
  this.isFieldset = (type === 'selectfieldset' || type === 'optionfieldset')
1970
1975
  && Array.isArray(this.layoutNode.items) && this.layoutNode.items.length > 0;
1971
- if (this.isFieldset && !this.options.titleMap && !this.options.enum) {
1976
+ this.initializeSelectList();
1977
+ this.initializeFieldsetValueMap();
1978
+ this.initializeControl();
1979
+ this.selectInitialFieldsetValue();
1980
+ }
1981
+ initializeSelectList() {
1982
+ if (this.isFieldset && !this.hasExplicitFieldsetOptions()) {
1972
1983
  this.selectList = this.layoutNode.items.map((item, i) => ({
1973
1984
  name: item.options?.legend || item.options?.title || item.name || `Option ${i + 1}`,
1974
1985
  value: i
@@ -1977,26 +1988,37 @@ class MaterialOneOfComponent {
1977
1988
  else {
1978
1989
  this.selectList = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, !!this.options.flatList);
1979
1990
  }
1980
- if (this.isFieldset && (this.options.titleMap || this.options.enum)) {
1981
- let idx = 0;
1982
- for (const entry of this.selectList) {
1983
- if (entry.value != null && entry.value !== '') {
1984
- if (idx === 0) {
1985
- this.selectedValue = entry.value;
1986
- }
1987
- this.fieldsetValueMap.set(entry.value, idx++);
1988
- }
1991
+ }
1992
+ initializeFieldsetValueMap() {
1993
+ if (!this.isFieldset || !this.hasExplicitFieldsetOptions()) {
1994
+ return;
1995
+ }
1996
+ let index = 0;
1997
+ for (const entry of this.selectList) {
1998
+ if (entry.value == null || entry.value === '') {
1999
+ continue;
2000
+ }
2001
+ if (index === 0) {
2002
+ this.selectedValue = entry.value;
1989
2003
  }
2004
+ this.fieldsetValueMap.set(entry.value, index++);
1990
2005
  }
1991
- if (!this.isFieldset || this.options.titleMap || this.options.enum) {
2006
+ }
2007
+ initializeControl() {
2008
+ if (!this.isFieldset || this.hasExplicitFieldsetOptions()) {
1992
2009
  this.jsf.initializeControl(this, !this.options.readonly);
1993
2010
  }
2011
+ }
2012
+ selectInitialFieldsetValue() {
1994
2013
  if (this.isFieldset && this.controlValue != null) {
1995
2014
  this.selectedValue = this.controlValue;
1996
2015
  this._selectedItem = this.fieldsetValueMap.get(this.controlValue)
1997
2016
  ?? (typeof this.controlValue === 'number' ? this.controlValue : 0);
1998
2017
  }
1999
2018
  }
2019
+ hasExplicitFieldsetOptions() {
2020
+ return this.options.titleMap || this.options.enum;
2021
+ }
2000
2022
  selectChild(event) {
2001
2023
  this.selectedValue = event.value;
2002
2024
  this._selectedItem =