@alauda-fe/crd-form 0.0.8 → 1.0.1-alpha.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 +7 -0
- package/fesm2022/alauda-fe-crd-form.mjs +3088 -0
- package/fesm2022/alauda-fe-crd-form.mjs.map +1 -0
- package/package.json +12 -14
- package/types/alauda-fe-crd-form.d.ts +934 -0
- package/esm2022/alauda-fe-crd-form.mjs +0 -5
- package/esm2022/lib/abstract/base-array.mjs +0 -117
- package/esm2022/lib/crd-form/component.mjs +0 -325
- package/esm2022/lib/crd-form/helper.mjs +0 -32
- package/esm2022/lib/crd-form.module.mjs +0 -115
- package/esm2022/lib/field-controls/expressions/expression-core.mjs +0 -2
- package/esm2022/lib/field-controls/expressions/props-expression.mjs +0 -58
- package/esm2022/lib/field-controls/form-item.component.mjs +0 -215
- package/esm2022/lib/field-controls/index.mjs +0 -8
- package/esm2022/lib/field-controls/module.mjs +0 -143
- package/esm2022/lib/field-controls/operand-advanced-field-group.component.mjs +0 -80
- package/esm2022/lib/field-controls/operand-array-field-group.component.mjs +0 -112
- package/esm2022/lib/field-controls/operand-field-group.component.mjs +0 -88
- package/esm2022/lib/field-controls/operand-field.component.mjs +0 -877
- package/esm2022/lib/field-controls/widgets/array-table/component.mjs +0 -109
- package/esm2022/lib/field-controls/widgets/basic-auth-secret/component.mjs +0 -116
- package/esm2022/lib/field-controls/widgets/basic-auth-secret/create/component.mjs +0 -211
- package/esm2022/lib/field-controls/widgets/basic-auth-secret/type.mjs +0 -2
- package/esm2022/lib/field-controls/widgets/index.mjs +0 -8
- package/esm2022/lib/field-controls/widgets/k8s-resource-prefix/component.mjs +0 -169
- package/esm2022/lib/field-controls/widgets/k8s-resource-prefix/util.mjs +0 -40
- package/esm2022/lib/field-controls/widgets/link/component.mjs +0 -68
- package/esm2022/lib/field-controls/widgets/resource-requirements/resource-requirements.component.mjs +0 -521
- package/esm2022/lib/field-controls/widgets/utils.mjs +0 -73
- package/esm2022/lib/field-controls/widgets/yaml-editor/yaml-editor.component.mjs +0 -94
- package/esm2022/lib/field-widgets/htpasswd/component.mjs +0 -40
- package/esm2022/lib/field-widgets/htpasswd/form.mjs +0 -240
- package/esm2022/lib/field-widgets/htpasswd/index.mjs +0 -4
- package/esm2022/lib/field-widgets/htpasswd/module.mjs +0 -54
- package/esm2022/lib/field-widgets/htpasswd/utils.mjs +0 -38
- package/esm2022/lib/field-widgets/index.mjs +0 -2
- package/esm2022/lib/remote-widgets/constants.mjs +0 -4
- package/esm2022/lib/remote-widgets/index.mjs +0 -4
- package/esm2022/lib/remote-widgets/remote-widget.service.mjs +0 -42
- package/esm2022/lib/remote-widgets/token.mjs +0 -7
- package/esm2022/lib/spec-descriptors/capability/capability.component.mjs +0 -139
- package/esm2022/lib/spec-descriptors/capability-list/capability-list.component.mjs +0 -189
- package/esm2022/lib/spec-descriptors/index.mjs +0 -4
- package/esm2022/lib/spec-descriptors/util.mjs +0 -13
- package/esm2022/lib/types/index.mjs +0 -89
- package/esm2022/lib/utils/capability.mjs +0 -306
- package/esm2022/lib/utils/constant.mjs +0 -22
- package/esm2022/lib/utils/helper.mjs +0 -196
- package/esm2022/lib/utils/index.mjs +0 -6
- package/esm2022/lib/utils/service-model.mjs +0 -12
- package/esm2022/lib/utils/util.mjs +0 -307
- package/esm2022/public-api.mjs +0 -13
- package/index.d.ts +0 -5
- package/lib/abstract/base-array.d.ts +0 -31
- package/lib/crd-form/component.d.ts +0 -58
- package/lib/crd-form/helper.d.ts +0 -7
- package/lib/crd-form.module.d.ts +0 -15
- package/lib/field-controls/expressions/expression-core.d.ts +0 -6
- package/lib/field-controls/expressions/props-expression.d.ts +0 -36
- package/lib/field-controls/form-item.component.d.ts +0 -19
- package/lib/field-controls/index.d.ts +0 -7
- package/lib/field-controls/module.d.ts +0 -23
- package/lib/field-controls/operand-advanced-field-group.component.d.ts +0 -16
- package/lib/field-controls/operand-array-field-group.component.d.ts +0 -8
- package/lib/field-controls/operand-field-group.component.d.ts +0 -21
- package/lib/field-controls/operand-field.component.d.ts +0 -109
- package/lib/field-controls/widgets/array-table/component.d.ts +0 -8
- package/lib/field-controls/widgets/basic-auth-secret/component.d.ts +0 -26
- package/lib/field-controls/widgets/basic-auth-secret/create/component.d.ts +0 -45
- package/lib/field-controls/widgets/basic-auth-secret/type.d.ts +0 -11
- package/lib/field-controls/widgets/index.d.ts +0 -7
- package/lib/field-controls/widgets/k8s-resource-prefix/component.d.ts +0 -35
- package/lib/field-controls/widgets/k8s-resource-prefix/util.d.ts +0 -3
- package/lib/field-controls/widgets/link/component.d.ts +0 -20
- package/lib/field-controls/widgets/resource-requirements/resource-requirements.component.d.ts +0 -85
- package/lib/field-controls/widgets/utils.d.ts +0 -17
- package/lib/field-controls/widgets/yaml-editor/yaml-editor.component.d.ts +0 -36
- package/lib/field-widgets/htpasswd/component.d.ts +0 -9
- package/lib/field-widgets/htpasswd/form.d.ts +0 -34
- package/lib/field-widgets/htpasswd/index.d.ts +0 -3
- package/lib/field-widgets/htpasswd/module.d.ts +0 -13
- package/lib/field-widgets/htpasswd/utils.d.ts +0 -15
- package/lib/field-widgets/index.d.ts +0 -1
- package/lib/remote-widgets/constants.d.ts +0 -3
- package/lib/remote-widgets/index.d.ts +0 -3
- package/lib/remote-widgets/remote-widget.service.d.ts +0 -28
- package/lib/remote-widgets/token.d.ts +0 -3
- package/lib/spec-descriptors/capability/capability.component.d.ts +0 -20
- package/lib/spec-descriptors/capability-list/capability-list.component.d.ts +0 -34
- package/lib/spec-descriptors/index.d.ts +0 -3
- package/lib/spec-descriptors/util.d.ts +0 -4
- package/lib/types/index.d.ts +0 -136
- package/lib/utils/capability.d.ts +0 -62
- package/lib/utils/constant.d.ts +0 -12
- package/lib/utils/helper.d.ts +0 -58
- package/lib/utils/index.d.ts +0 -5
- package/lib/utils/service-model.d.ts +0 -44
- package/lib/utils/util.d.ts +0 -44
- package/public-api.d.ts +0 -9
|
@@ -0,0 +1,3088 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, forwardRef, ChangeDetectorRef, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, ViewChild, Injector, HostBinding, Directive, NgModule } from '@angular/core';
|
|
3
|
+
import { escapeRegExp, get, includes, some, cloneDeep, map, reduce, find, set, last, isEmpty, identity, toPath, flatten, times, isArray, flatMap, flatMapDepth, union, pick, startCase, parseInt as parseInt$1, uniqWith, groupBy, sortBy, omit, merge, template, differenceBy, uniq, flattenDeep, fromPairs, toString } from 'lodash-es';
|
|
4
|
+
import { __decorate, __metadata } from 'tslib';
|
|
5
|
+
import * as i2 from '@alauda/ui';
|
|
6
|
+
import { FormModule, IconModule, TooltipModule, ButtonModule, DIALOG_DATA, DialogRef, MessageService, InputModule, DialogModule, DialogService, DialogSize, SelectModule, TagModule, RadioModule, SwitchModule, LabelPosition } from '@alauda/ui';
|
|
7
|
+
import * as i4 from '@alauda-fe/dynamic-plugin-sdk';
|
|
8
|
+
import { parseJSONStream, TRUE, FALSE, parseJson, isEqual, PurePipe, TranslatePipe, K8sApiService, TranslateService, K8S_RESOURCE_NAME_BASE, SecretType, COMMON_RESOURCE_DEFINITIONS, K8sUtilService, publishRef, skipError, ObservableInput, K8S_UTIL_PIPES_MODULE, TOKEN_RESOURCE_DEFINITIONS, ValueHook, SanitizePipe, FieldNotAvailablePipe, bind, stringify, parse, API_GATEWAY, startWithCondition } from '@alauda-fe/dynamic-plugin-sdk';
|
|
9
|
+
import * as i2$1 from '@alauda-fe/dynamic-plugin-shared';
|
|
10
|
+
import { isJsonObjectString, ErrorsMapperComponent, HelpDocDirective, ARRAY_FORM_TABLE_MODULE, StrongPasswordDirective, createNestedFormControl, formatCPU, formatMemory, resourceUnits, getResourceValue, getResourceViewModel, initGreaterValidator, transferResource, RESOURCE_MAC_TYPES, PasswordInputComponent, ReadonlyFieldDirective, ParseJsonTranslatePipe, ScrollToFirstInvalidDirective, ErrorsMapperDirective } from '@alauda-fe/dynamic-plugin-shared';
|
|
11
|
+
import * as i1 from '@angular/forms';
|
|
12
|
+
import { FormBuilder, Validators, FormsModule, ReactiveFormsModule, FormGroupDirective, NG_VALUE_ACCESSOR, ControlContainer, FormControl } from '@angular/forms';
|
|
13
|
+
import { isImmutable, fromJS, Map as Map$1, List } from 'immutable';
|
|
14
|
+
import { finalize, filter, map as map$1, Subject, combineLatest, startWith, switchMap, takeUntil, Observable, BehaviorSubject, pluck, tap, of, debounceTime, distinctUntilChanged, isObservable, take } from 'rxjs';
|
|
15
|
+
import { CodeEditorComponent, yamlWriteOptions, createActions, viewActions, yamlReadOptions } from '@alauda/code-editor';
|
|
16
|
+
import * as i1$1 from '@angular/common';
|
|
17
|
+
import { CommonModule } from '@angular/common';
|
|
18
|
+
import { HttpClient } from '@angular/common/http';
|
|
19
|
+
import { encode, decode } from 'ab64';
|
|
20
|
+
import { BaseResourceFormComponent, BaseResourceFormGroupComponent } from 'ng-resource-form-util';
|
|
21
|
+
import { hashSync } from 'bcryptjs';
|
|
22
|
+
|
|
23
|
+
var Validations;
|
|
24
|
+
(function (Validations) {
|
|
25
|
+
Validations["maximum"] = "maximum";
|
|
26
|
+
Validations["minimum"] = "minimum";
|
|
27
|
+
Validations["maxLength"] = "maxLength";
|
|
28
|
+
Validations["minLength"] = "minLength";
|
|
29
|
+
Validations["pattern"] = "pattern";
|
|
30
|
+
Validations["required"] = "required";
|
|
31
|
+
})(Validations || (Validations = {}));
|
|
32
|
+
const SpecCapabilityUIPrefix = 'urn:alm:descriptor:com.tectonic.ui';
|
|
33
|
+
const SpecCapabilityWidgetsPrefix = 'urn:alm:descriptor:widgets';
|
|
34
|
+
var SpecCapability;
|
|
35
|
+
(function (SpecCapability) {
|
|
36
|
+
/**
|
|
37
|
+
* 表单 UI 控件
|
|
38
|
+
*/
|
|
39
|
+
// 基础 UI 控件
|
|
40
|
+
SpecCapability["text"] = "urn:alm:descriptor:com.tectonic.ui:text";
|
|
41
|
+
SpecCapability["textarea"] = "urn:alm:descriptor:com.tectonic.ui:textarea";
|
|
42
|
+
SpecCapability["yaml"] = "urn:alm:descriptor:com.tectonic.ui:yaml";
|
|
43
|
+
SpecCapability["number"] = "urn:alm:descriptor:com.tectonic.ui:number";
|
|
44
|
+
SpecCapability["podCount"] = "urn:alm:descriptor:com.tectonic.ui:podCount";
|
|
45
|
+
SpecCapability["booleanSwitch"] = "urn:alm:descriptor:com.tectonic.ui:booleanSwitch";
|
|
46
|
+
SpecCapability["radio"] = "urn:alm:descriptor:com.tectonic.ui:radio:";
|
|
47
|
+
SpecCapability["tagsInput"] = "urn:alm:descriptor:com.tectonic.ui:tagsInput";
|
|
48
|
+
SpecCapability["select"] = "urn:alm:descriptor:com.tectonic.ui:select:";
|
|
49
|
+
SpecCapability["password"] = "urn:alm:descriptor:com.tectonic.ui:password";
|
|
50
|
+
SpecCapability["confirmPassword"] = "urn:alm:descriptor:com.tectonic.ui:password:confirm";
|
|
51
|
+
SpecCapability["externalPassword"] = "urn:alm:descriptor:com.tectonic.ui:externalPassword";
|
|
52
|
+
// 高级 UI 控件 widgets
|
|
53
|
+
SpecCapability["widgets"] = "urn:alm:descriptor:widgets:";
|
|
54
|
+
SpecCapability["remote"] = "urn:alm:descriptor:remote:";
|
|
55
|
+
SpecCapability["k8sResourcePrefix"] = "urn:alm:descriptor:io.kubernetes:";
|
|
56
|
+
SpecCapability["basicAuthSecret"] = "urn:alm:descriptor:com.tectonic.ui:basicAuthSecret";
|
|
57
|
+
SpecCapability["resourceRequirements"] = "urn:alm:descriptor:com.tectonic.ui:resourceRequirements";
|
|
58
|
+
SpecCapability["arrayFieldGroup"] = "urn:alm:descriptor:com.tectonic.ui:arrayFieldGroup:";
|
|
59
|
+
SpecCapability["arrayTable"] = "urn:alm:descriptor:widgets:common:arrayTable";
|
|
60
|
+
SpecCapability["array"] = "urn:alm:descriptor:com.tectonic.ui:array";
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated
|
|
63
|
+
*/
|
|
64
|
+
SpecCapability["object"] = "urn:alm:descriptor:com.tectonic.ui:object";
|
|
65
|
+
/**
|
|
66
|
+
* aui-form 表单元素
|
|
67
|
+
*/
|
|
68
|
+
SpecCapability["label"] = "urn:alm:descriptor:label:";
|
|
69
|
+
SpecCapability["description"] = "urn:alm:descriptor:description:";
|
|
70
|
+
SpecCapability["placeholder"] = "urn:alm:descriptor:placeholder:";
|
|
71
|
+
SpecCapability["tooltip"] = "urn:alm:descriptor:tooltip:";
|
|
72
|
+
SpecCapability["descriptionAsLink"] = "urn:alm:descriptor:descriptionAsLink";
|
|
73
|
+
SpecCapability["docOption"] = "urn:alm:descriptor:docOption:";
|
|
74
|
+
SpecCapability["docLink"] = "urn:alm:descriptor:docLink:";
|
|
75
|
+
SpecCapability["validation"] = "urn:alm:descriptor:com.tectonic.ui:validation:";
|
|
76
|
+
SpecCapability["copyable"] = "urn:alm:descriptor:copyable";
|
|
77
|
+
/**
|
|
78
|
+
* 表单布局/控制能力
|
|
79
|
+
*/
|
|
80
|
+
SpecCapability["hidden"] = "urn:alm:descriptor:com.tectonic.ui:hidden";
|
|
81
|
+
SpecCapability["defaultValue"] = "urn:alm:descriptor:com.tectonic.default:";
|
|
82
|
+
SpecCapability["fieldGroup"] = "urn:alm:descriptor:com.tectonic.ui:fieldGroup:";
|
|
83
|
+
SpecCapability["advanced"] = "urn:alm:descriptor:com.tectonic.ui:advanced";
|
|
84
|
+
SpecCapability["fieldDependency"] = "urn:alm:descriptor:com.tectonic.ui:fieldDependency:";
|
|
85
|
+
SpecCapability["oneOf"] = "urn:alm:descriptor:oneOf:";
|
|
86
|
+
/**
|
|
87
|
+
* UI 控件特性扩展
|
|
88
|
+
*/
|
|
89
|
+
SpecCapability["expression"] = "urn:alm:descriptor:expression:";
|
|
90
|
+
// urn:alm:descriptor:yaml:actions:disabled:diffMode,recover
|
|
91
|
+
SpecCapability["yamlActionsDisabled"] = "urn:alm:descriptor:yaml:actions:disabled:";
|
|
92
|
+
// urn:alm:descriptor:yaml:result:json
|
|
93
|
+
// urn:alm:descriptor:yaml:result:stringify
|
|
94
|
+
SpecCapability["yamlResult"] = "urn:alm:descriptor:yaml:result:";
|
|
95
|
+
// object 和 array 组件折叠, 默认为展开
|
|
96
|
+
SpecCapability["folded"] = "urn:alm:descriptor:com.tectonic.ui:folded";
|
|
97
|
+
// array 组件在没有 Value 的前提下支持不保留首项
|
|
98
|
+
SpecCapability["emptyArray"] = "urn:alm:descriptor:com.tectonic.ui:emptyArray";
|
|
99
|
+
// 用于支持复数场景,如 multiselect,tags-input 等
|
|
100
|
+
SpecCapability["multiple"] = "urn:alm:descriptor:com.tectonic.ui:multiple";
|
|
101
|
+
SpecCapability["allowCreate"] = "urn:alm:descriptor:com.tectonic.ui:allowCreate";
|
|
102
|
+
SpecCapability["creatable"] = "urn:alm:descriptor:com.tectonic.ui:creatable";
|
|
103
|
+
SpecCapability["clearable"] = "urn:alm:descriptor:com.tectonic.ui:clearable";
|
|
104
|
+
SpecCapability["disabled"] = "urn:alm:descriptor:com.tectonic.ui:disabled";
|
|
105
|
+
/**
|
|
106
|
+
* 其他
|
|
107
|
+
*/
|
|
108
|
+
// 用于做control唯一识别的依据,缺省情况下 path 将作为依据
|
|
109
|
+
SpecCapability["pathTag"] = "urn:alm:descriptor:com.tectonic.ui:path.tag:";
|
|
110
|
+
})(SpecCapability || (SpecCapability = {}));
|
|
111
|
+
|
|
112
|
+
const NodeModel = {
|
|
113
|
+
apiVersion: 'v1',
|
|
114
|
+
label: 'Node',
|
|
115
|
+
labelKey: 'public~Node',
|
|
116
|
+
plural: 'nodes',
|
|
117
|
+
abbr: 'N',
|
|
118
|
+
kind: 'Node',
|
|
119
|
+
id: 'node',
|
|
120
|
+
labelPlural: 'Nodes',
|
|
121
|
+
labelPluralKey: 'public~Nodes',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
var ServiceModels = /*#__PURE__*/Object.freeze({
|
|
125
|
+
__proto__: null,
|
|
126
|
+
NodeModel: NodeModel
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const MAX_DEPTH = 1;
|
|
130
|
+
const SCHEMA_PATH = ['properties', 'spec'];
|
|
131
|
+
const DESCRIPTOR_DEFINITION_KEY = 'x-descriptors';
|
|
132
|
+
const NamespacedResourceKind = Object.values(ServiceModels)
|
|
133
|
+
.filter(i => i.namespaced)
|
|
134
|
+
.map(i => i.kind);
|
|
135
|
+
// Regex for SpecCapability.arrayFieldGroup and SpecCapability.fieldGroup
|
|
136
|
+
const ARRAY_FIELD_GROUP_PATTERN = escapeRegExp(SpecCapability.arrayFieldGroup);
|
|
137
|
+
const FIELD_GROUP_PATTERN = escapeRegExp(SpecCapability.fieldGroup);
|
|
138
|
+
const GROUP_PATTERN = new RegExp(`^(${FIELD_GROUP_PATTERN}|${ARRAY_FIELD_GROUP_PATTERN})(.*)$`);
|
|
139
|
+
const DEFAULT_VALUE_PATTERN = new RegExp(`^${escapeRegExp(SpecCapability.defaultValue)}(.*)$`);
|
|
140
|
+
const REGEXP_K8S_RESOURCE_CAPABILITY = escapeRegExp(SpecCapability.k8sResourcePrefix);
|
|
141
|
+
const REGEXP_CAPABILITY_VALIDATION = escapeRegExp(SpecCapability.validation);
|
|
142
|
+
const REGEXP_FIELD_DEPENDENCY_CAPABILITY = escapeRegExp(SpecCapability.fieldDependency);
|
|
143
|
+
const REGEXP_SELECT_CAPABILITY = escapeRegExp(SpecCapability.select);
|
|
144
|
+
const REGEXP_K8S_RESOURCE_SUFFIX = new RegExp(`^${REGEXP_K8S_RESOURCE_CAPABILITY}(?:core[:~]v1[:~])?([^?]*)(?:\\?\\+)?([^?]*)?(?:\\?\\=)?([^?]*)?$`);
|
|
145
|
+
const SupportValidation = Object.values(Validations).join('|');
|
|
146
|
+
const REGEXP_VALIDATION_SUFFIX = new RegExp(`^${REGEXP_CAPABILITY_VALIDATION}(${SupportValidation})(?::(.+))?$`);
|
|
147
|
+
|
|
148
|
+
const isRequired = (field) => field?.required ||
|
|
149
|
+
Object.keys(field?.validations || {})?.includes(Validations.required);
|
|
150
|
+
const filterMapByKey = (map, predicate) => {
|
|
151
|
+
const m = new Map();
|
|
152
|
+
map.forEach((v, k) => {
|
|
153
|
+
if (predicate(k)) {
|
|
154
|
+
m.set(k, v);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return m;
|
|
158
|
+
};
|
|
159
|
+
const getMatchArrayIndex = (arrPath) => {
|
|
160
|
+
const matched = arrPath.match(/(^.*)\[(\d+)]$/);
|
|
161
|
+
return +get(matched, '[2]', 0);
|
|
162
|
+
};
|
|
163
|
+
const isGroupField = (field) => field.type === 'object';
|
|
164
|
+
const isArrayField = (field) => field.type === 'array';
|
|
165
|
+
const isRemoteField = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.remote));
|
|
166
|
+
const isArrayFieldTable = (field) => isArrayField(field) &&
|
|
167
|
+
field.capabilities.some(c => c.startsWith(SpecCapability.arrayTable));
|
|
168
|
+
const isCreatable = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.creatable));
|
|
169
|
+
const isMultiple = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.multiple));
|
|
170
|
+
const isAllowCreate = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.allowCreate));
|
|
171
|
+
const isClearable = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.clearable));
|
|
172
|
+
const isFolded = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.folded));
|
|
173
|
+
const isEmptyArray = (field) => field.capabilities.some(c => c.startsWith(SpecCapability.emptyArray));
|
|
174
|
+
function hasDescriptor(field, prefix, suffix = null) {
|
|
175
|
+
return suffix
|
|
176
|
+
? includes(field.capabilities, `${prefix}${suffix}`)
|
|
177
|
+
: some(field.capabilities, capability => capability.startsWith(prefix));
|
|
178
|
+
}
|
|
179
|
+
/*
|
|
180
|
+
* Matches a path that contains an array index. Use Sting.match against an OperandField 'path'
|
|
181
|
+
* property to determine if it contains an array index. It will parse the path into three parts,
|
|
182
|
+
* [match, pathBeforeIndex, index, pathAfterIndex]. For example:
|
|
183
|
+
*
|
|
184
|
+
* const [match, pathBeforeIndex, index, pathAfterIndex] =
|
|
185
|
+
* 'path.before[0].path.after'.match(ARRAY_INDEX_PATTERN);
|
|
186
|
+
*
|
|
187
|
+
* console.log(match);
|
|
188
|
+
* > 'path.before[0].path.after'
|
|
189
|
+
*
|
|
190
|
+
* console.log(pathBeforeIndex);
|
|
191
|
+
* > 'path.before'
|
|
192
|
+
*
|
|
193
|
+
* console.log(index)
|
|
194
|
+
* > '0'
|
|
195
|
+
*
|
|
196
|
+
* console.log(pathAfterIndex)
|
|
197
|
+
* > 'path.after'
|
|
198
|
+
*
|
|
199
|
+
*/
|
|
200
|
+
const ARRAY_INDEX_PATTERN = /^(.*)\[(\d+)]\.?(.*)$/;
|
|
201
|
+
function parseArrayPath(path) {
|
|
202
|
+
const [match, pathBeforeIndex, index, pathAfterIndex] = ARRAY_INDEX_PATTERN.exec(path) || [];
|
|
203
|
+
return match
|
|
204
|
+
? { index: parseInt(index, 10), match, pathBeforeIndex, pathAfterIndex }
|
|
205
|
+
: { match };
|
|
206
|
+
}
|
|
207
|
+
function getArrayGroupName(field) {
|
|
208
|
+
if (hasDescriptor(field, SpecCapability.arrayFieldGroup)) {
|
|
209
|
+
return field.capabilities
|
|
210
|
+
.find(capability => capability.startsWith(SpecCapability.arrayFieldGroup))
|
|
211
|
+
.slice(SpecCapability.arrayFieldGroup.length);
|
|
212
|
+
}
|
|
213
|
+
return '';
|
|
214
|
+
}
|
|
215
|
+
function getGroupName(field) {
|
|
216
|
+
if (hasDescriptor(field, SpecCapability.fieldGroup)) {
|
|
217
|
+
return field.capabilities
|
|
218
|
+
.find(capability => capability.startsWith(SpecCapability.fieldGroup))
|
|
219
|
+
.slice(SpecCapability.fieldGroup.length);
|
|
220
|
+
}
|
|
221
|
+
return '';
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 递归修复树形结构中 数组类型的索引
|
|
225
|
+
*
|
|
226
|
+
* @export
|
|
227
|
+
* @param {OperandField} field
|
|
228
|
+
* @param {string} fromPath
|
|
229
|
+
* @param {string} toPath
|
|
230
|
+
* @returns
|
|
231
|
+
*
|
|
232
|
+
*/
|
|
233
|
+
const repairFieldChildPathByCorrectIndex = (field, fromPath, toPath) => {
|
|
234
|
+
const targetField = cloneDeep(field);
|
|
235
|
+
if (isGroupField(targetField)) {
|
|
236
|
+
targetField.fieldList = map(targetField.fieldList, f => ({
|
|
237
|
+
...f,
|
|
238
|
+
path: f.path.replace(fromPath, toPath),
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
else if (isArrayField(targetField)) {
|
|
242
|
+
targetField.fieldLists = reduce(targetField.fieldLists, (t, c) => [
|
|
243
|
+
...t,
|
|
244
|
+
map(c, f => repairFieldChildPathByCorrectIndex(f, fromPath, toPath)),
|
|
245
|
+
], []);
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
...targetField,
|
|
249
|
+
path: targetField.path.replace(fromPath, toPath),
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
const getRelatedFields = (normalFields) => map(normalFields, field => {
|
|
253
|
+
if (getArrayGroupName(field)) {
|
|
254
|
+
const { pathBeforeIndex, index } = parseArrayPath(field.path);
|
|
255
|
+
return {
|
|
256
|
+
...field,
|
|
257
|
+
parent: pathBeforeIndex,
|
|
258
|
+
inParentIndex: index,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (getGroupName(field)) {
|
|
262
|
+
const groupNameFromDefinition = `spec.${getGroupName(field)}`;
|
|
263
|
+
const parent = field.path.slice(0, groupNameFromDefinition.length);
|
|
264
|
+
return {
|
|
265
|
+
...field,
|
|
266
|
+
parent,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return field;
|
|
270
|
+
});
|
|
271
|
+
const getTreeFields = (rootFields, basicFields) => {
|
|
272
|
+
if (basicFields.length > 0) {
|
|
273
|
+
return rootFields.reduce((acc, field) => {
|
|
274
|
+
field._dirty = true;
|
|
275
|
+
const children = basicFields.filter(f => f.parent === field.path);
|
|
276
|
+
children.forEach(c => (c._dirty = true));
|
|
277
|
+
if (isArrayField(field)) {
|
|
278
|
+
field.fieldLists = children
|
|
279
|
+
.reduce((acc, child) => {
|
|
280
|
+
acc[child.inParentIndex] = [
|
|
281
|
+
...(acc[child.inParentIndex] || []),
|
|
282
|
+
child,
|
|
283
|
+
];
|
|
284
|
+
return acc;
|
|
285
|
+
}, [])
|
|
286
|
+
.map(m => getTreeFields(m, basicFields.filter(f => !f._dirty)));
|
|
287
|
+
}
|
|
288
|
+
else if (isGroupField(field)) {
|
|
289
|
+
field.fieldList = getTreeFields(children, basicFields.filter(f => !f._dirty));
|
|
290
|
+
}
|
|
291
|
+
return [...acc, field];
|
|
292
|
+
}, []);
|
|
293
|
+
}
|
|
294
|
+
return rootFields.map(f => {
|
|
295
|
+
f._dirty = true;
|
|
296
|
+
return f;
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
const getMatchedCapabilityValue = (capabilities, descriptor) => capabilities
|
|
300
|
+
.find(capability => capability.startsWith(descriptor))
|
|
301
|
+
?.slice(descriptor.length) || '';
|
|
302
|
+
function evalInContext(stringExpression, context) {
|
|
303
|
+
// replace eval, skip eslint & sonar safe check
|
|
304
|
+
function evil(fn) {
|
|
305
|
+
const Fn = Function;
|
|
306
|
+
return new Fn(`return ${fn}`);
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
return evil(stringExpression).call(context);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
console.error('expression is invalid:', stringExpression);
|
|
313
|
+
return stringExpression;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function coerceBoolean(val) {
|
|
317
|
+
return val === 'true';
|
|
318
|
+
}
|
|
319
|
+
function coerceNumber(val, fallbackValue = 0) {
|
|
320
|
+
return _isNumberValue(val) ? Number(val) : fallbackValue;
|
|
321
|
+
}
|
|
322
|
+
function _isNumberValue(value) {
|
|
323
|
+
// parseFloat(value) handles most of the cases we're interested in (it treats null, empty string,
|
|
324
|
+
// and other non-number values as NaN, where Number just uses 0) but it considers the string
|
|
325
|
+
// '123hello' to be a valid number. Therefore we also check if Number(value) is NaN.
|
|
326
|
+
return (!isNaN(typeof value === 'string' && parseFloat(value)) &&
|
|
327
|
+
!isNaN(Number(value)));
|
|
328
|
+
}
|
|
329
|
+
function getOperandPath(field) {
|
|
330
|
+
const idField = field?.capabilities.find(c => c.startsWith(SpecCapability.pathTag));
|
|
331
|
+
if (idField) {
|
|
332
|
+
return `${field.path}@${idField.slice(SpecCapability.pathTag.length)}`;
|
|
333
|
+
}
|
|
334
|
+
return field.path;
|
|
335
|
+
}
|
|
336
|
+
function getPathFromTagPath(pathWithTag) {
|
|
337
|
+
return pathWithTag.split('@')[0];
|
|
338
|
+
}
|
|
339
|
+
// 获取path下所有的filed,例如spec.a 能匹配到 spec.a.a1 spec.a.a2
|
|
340
|
+
const getFieldsByPath = (path, fields) => fields.filter(field => getOperandPath(field).startsWith(path));
|
|
341
|
+
|
|
342
|
+
function getBasicAuthSecretConfig(field) {
|
|
343
|
+
const configPrefix = `${SpecCapability.basicAuthSecret}:`;
|
|
344
|
+
const capability = find(field.capabilities, descriptor => descriptor.startsWith(configPrefix));
|
|
345
|
+
if (!capability) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
const configStr = capability.slice(configPrefix.length);
|
|
349
|
+
return parseJSONStream(configStr)[0];
|
|
350
|
+
}
|
|
351
|
+
function getRadioOptions(capabilities) {
|
|
352
|
+
const items = capabilities
|
|
353
|
+
.filter(c => c.startsWith(SpecCapability.radio))
|
|
354
|
+
.map(c => c.split(SpecCapability.radio)[1]);
|
|
355
|
+
return items
|
|
356
|
+
.filter(item => item.split(':').length === 1)
|
|
357
|
+
.map(item => {
|
|
358
|
+
const zhPrefix = `${item}:zh:`;
|
|
359
|
+
const enPrefix = `${item}:en:`;
|
|
360
|
+
const zhDescriptor = items.find(value => value.startsWith(zhPrefix));
|
|
361
|
+
const enDescriptor = items.find(value => value.startsWith(enPrefix));
|
|
362
|
+
return {
|
|
363
|
+
value: item,
|
|
364
|
+
display: {
|
|
365
|
+
zh: zhDescriptor ? zhDescriptor.split(zhPrefix)[1] : item,
|
|
366
|
+
en: enDescriptor ? enDescriptor.split(enPrefix)[1] : item,
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function setRadiosDefaultValue(descriptors) {
|
|
372
|
+
// Set radio default selected item
|
|
373
|
+
const defaultValueMap = (descriptors || [])
|
|
374
|
+
.filter(d => d[DESCRIPTOR_DEFINITION_KEY].some(item => item.startsWith(SpecCapability.radio)))
|
|
375
|
+
.map(item => ({
|
|
376
|
+
path: item.path,
|
|
377
|
+
value: item[DESCRIPTOR_DEFINITION_KEY].filter(c => c.startsWith(SpecCapability.radio))
|
|
378
|
+
.map(c => c.split(SpecCapability.radio)[1])
|
|
379
|
+
.find(c => c.split(':').length === 1),
|
|
380
|
+
}))
|
|
381
|
+
.reduce((acc, cur) => ({
|
|
382
|
+
...set(acc, cur.path, cur.value),
|
|
383
|
+
}), {});
|
|
384
|
+
return {
|
|
385
|
+
spec: {
|
|
386
|
+
...defaultValueMap,
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const getBooleanSwitchOptions = (capabilities) => {
|
|
391
|
+
const items = capabilities
|
|
392
|
+
.filter(c => c.startsWith(SpecCapability.booleanSwitch))
|
|
393
|
+
.map(c => c.split(SpecCapability.booleanSwitch)[1]);
|
|
394
|
+
return [TRUE, FALSE].map(item => {
|
|
395
|
+
const zhPrefix = `:${item}:zh:`;
|
|
396
|
+
const enPrefix = `:${item}:en:`;
|
|
397
|
+
const zhDescriptor = items.find(value => value.startsWith(zhPrefix));
|
|
398
|
+
const enDescriptor = items.find(value => value.startsWith(enPrefix));
|
|
399
|
+
return {
|
|
400
|
+
value: item,
|
|
401
|
+
display: zhDescriptor && enDescriptor
|
|
402
|
+
? {
|
|
403
|
+
zh: zhDescriptor ? zhDescriptor.split(zhPrefix)[1] : item,
|
|
404
|
+
en: enDescriptor ? enDescriptor.split(enPrefix)[1] : item,
|
|
405
|
+
}
|
|
406
|
+
: item === TRUE
|
|
407
|
+
? 'yes'
|
|
408
|
+
: 'no',
|
|
409
|
+
};
|
|
410
|
+
});
|
|
411
|
+
};
|
|
412
|
+
const convertBooleanSwitchValue = (value, capabilities) => {
|
|
413
|
+
const options = getBooleanSwitchOptions(capabilities);
|
|
414
|
+
return options.find(item => item.value === (value ? TRUE : FALSE))?.display;
|
|
415
|
+
};
|
|
416
|
+
function convertValue(value, type) {
|
|
417
|
+
switch (type) {
|
|
418
|
+
case 'number':
|
|
419
|
+
case 'integer': {
|
|
420
|
+
return coerceNumber(value);
|
|
421
|
+
}
|
|
422
|
+
case 'boolean': {
|
|
423
|
+
return coerceBoolean(value);
|
|
424
|
+
}
|
|
425
|
+
default: {
|
|
426
|
+
// 兼容 value 为 json 字符串的情况
|
|
427
|
+
return isJsonObjectString(value)
|
|
428
|
+
? parseJson(value, value)
|
|
429
|
+
: value || undefined;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function getDefaultValue(field) {
|
|
434
|
+
const defaultValueDescriptor = find(field.capabilities, descriptor => descriptor.startsWith(SpecCapability.defaultValue));
|
|
435
|
+
if (defaultValueDescriptor) {
|
|
436
|
+
const [, value] = DEFAULT_VALUE_PATTERN.exec(defaultValueDescriptor) || [];
|
|
437
|
+
return convertValue(value, field.type);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const UI_FIELDS = [
|
|
441
|
+
SpecCapability.text,
|
|
442
|
+
{
|
|
443
|
+
spec: `${SpecCapability.text}:link`,
|
|
444
|
+
refer: SpecCapability.text,
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
spec: `${SpecCapability.text}:link:`,
|
|
448
|
+
prefix: true,
|
|
449
|
+
},
|
|
450
|
+
SpecCapability.password,
|
|
451
|
+
SpecCapability.confirmPassword,
|
|
452
|
+
SpecCapability.externalPassword,
|
|
453
|
+
SpecCapability.number,
|
|
454
|
+
SpecCapability.textarea,
|
|
455
|
+
{
|
|
456
|
+
spec: SpecCapability.podCount,
|
|
457
|
+
refer: SpecCapability.number,
|
|
458
|
+
},
|
|
459
|
+
SpecCapability.booleanSwitch,
|
|
460
|
+
{
|
|
461
|
+
spec: SpecCapability.select,
|
|
462
|
+
prefix: true,
|
|
463
|
+
},
|
|
464
|
+
SpecCapability.resourceRequirements,
|
|
465
|
+
SpecCapability.yaml,
|
|
466
|
+
SpecCapability.object,
|
|
467
|
+
SpecCapability.array,
|
|
468
|
+
{
|
|
469
|
+
spec: SpecCapability.radio,
|
|
470
|
+
prefix: true,
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
spec: SpecCapability.k8sResourcePrefix,
|
|
474
|
+
prefix: true,
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
spec: SpecCapability.basicAuthSecret,
|
|
478
|
+
prefix: true,
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
spec: SpecCapability.widgets,
|
|
482
|
+
prefix: true,
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
spec: SpecCapability.remote,
|
|
486
|
+
prefix: true,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
spec: SpecCapability.tagsInput,
|
|
490
|
+
prefix: true,
|
|
491
|
+
},
|
|
492
|
+
];
|
|
493
|
+
function getFieldType(field) {
|
|
494
|
+
const { capabilities } = field;
|
|
495
|
+
const result = UI_FIELDS.find(specField => {
|
|
496
|
+
if (typeof specField === 'string') {
|
|
497
|
+
return capabilities.includes(specField);
|
|
498
|
+
}
|
|
499
|
+
return specField.prefix
|
|
500
|
+
? capabilities.some(c => c.startsWith(specField.spec))
|
|
501
|
+
: capabilities.includes(specField.spec);
|
|
502
|
+
});
|
|
503
|
+
return typeof result === 'string' ? result : (result?.refer ?? result?.spec);
|
|
504
|
+
}
|
|
505
|
+
function getBasicCapabilityType(capabilities) {
|
|
506
|
+
if (capabilities.includes(SpecCapability.text)) {
|
|
507
|
+
return 'string';
|
|
508
|
+
}
|
|
509
|
+
if (capabilities.includes(SpecCapability.number) ||
|
|
510
|
+
capabilities.includes(SpecCapability.podCount)) {
|
|
511
|
+
return 'number';
|
|
512
|
+
}
|
|
513
|
+
if (capabilities.includes(SpecCapability.booleanSwitch)) {
|
|
514
|
+
return 'boolean';
|
|
515
|
+
}
|
|
516
|
+
if (capabilities.includes(SpecCapability.object)) {
|
|
517
|
+
return 'object';
|
|
518
|
+
}
|
|
519
|
+
if (capabilities.includes(SpecCapability.array)) {
|
|
520
|
+
return 'array';
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function getDisplayByCapability(capabilities, name, fallback) {
|
|
524
|
+
const enDes = `${name}en:`;
|
|
525
|
+
const zhDes = `${name}zh:`;
|
|
526
|
+
const en = find(capabilities, descriptor => descriptor.startsWith(enDes))?.split(enDes)?.[1];
|
|
527
|
+
const zh = find(capabilities, descriptor => descriptor.startsWith(zhDes))?.split(zhDes)?.[1];
|
|
528
|
+
return en || zh
|
|
529
|
+
? { en: en || zh || fallback, zh: zh || en || fallback }
|
|
530
|
+
: fallback
|
|
531
|
+
? { en: fallback, zh: fallback }
|
|
532
|
+
: null;
|
|
533
|
+
}
|
|
534
|
+
function getFieldDisplayName(field) {
|
|
535
|
+
return getDisplayByCapability(field.capabilities, SpecCapability.label, field.displayName ?? '');
|
|
536
|
+
}
|
|
537
|
+
function getFieldDescription$1(field) {
|
|
538
|
+
return getDisplayByCapability(field.capabilities, SpecCapability.description, field.description ?? '');
|
|
539
|
+
}
|
|
540
|
+
function getFieldPlaceholder$1(field) {
|
|
541
|
+
return getDisplayByCapability(field.capabilities, SpecCapability.placeholder);
|
|
542
|
+
}
|
|
543
|
+
function getFieldTooltip(field) {
|
|
544
|
+
return getDisplayByCapability(field.capabilities, SpecCapability.tooltip);
|
|
545
|
+
}
|
|
546
|
+
function isFieldCopyable(field) {
|
|
547
|
+
return !!find(field.capabilities, descriptor => descriptor === SpecCapability.copyable);
|
|
548
|
+
}
|
|
549
|
+
function isFieldDescriptionAsLink(field) {
|
|
550
|
+
return !!find(field.capabilities, descriptor => descriptor === SpecCapability.descriptionAsLink);
|
|
551
|
+
}
|
|
552
|
+
function getFieldDocLink(field) {
|
|
553
|
+
const docLinkDescriptor = find(field.capabilities, descriptor => descriptor.startsWith(SpecCapability.docLink));
|
|
554
|
+
if (docLinkDescriptor) {
|
|
555
|
+
return docLinkDescriptor.split(SpecCapability.docLink)[1];
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// cspell:disable-next-line
|
|
559
|
+
// k8s params should conform to urn:alm:descriptor:io.kubernetes:Secret?=isGlobal=true:labelSelector=plugins.cpaas.io%2Fvictoriametrics%3Dtrue
|
|
560
|
+
// : is used by dividing meta params and queryParams,both should be url encoded(percent encoding)
|
|
561
|
+
const K8S_RESOURCE_META_PARAMS_DIVIDER = ':';
|
|
562
|
+
function getK8sResourcePrefixOptions(field) {
|
|
563
|
+
const capability = find(field.capabilities, descriptor => descriptor.startsWith(SpecCapability.k8sResourcePrefix));
|
|
564
|
+
const multiple = field.capabilities.some(descriptor => descriptor.startsWith(SpecCapability.multiple));
|
|
565
|
+
const validations = getCapabilityValidations(field.capabilities);
|
|
566
|
+
const [, resource, , qComponents] = capability.match(REGEXP_K8S_RESOURCE_SUFFIX) ?? [];
|
|
567
|
+
const groupVersionKind = resource?.replace(/:/g, '~');
|
|
568
|
+
const kind = last(groupVersionKind.split('~'));
|
|
569
|
+
const [metaStr, queryParamsStr] = (qComponents ?? '').split(K8S_RESOURCE_META_PARAMS_DIVIDER);
|
|
570
|
+
const meta = resolveKVParams(metaStr);
|
|
571
|
+
const queryParams = resolveKVParams(queryParamsStr);
|
|
572
|
+
return {
|
|
573
|
+
namespaced: NamespacedResourceKind.includes(kind),
|
|
574
|
+
labelSelector: queryParams.labelSelector,
|
|
575
|
+
isGlobal: !!meta.isGlobal,
|
|
576
|
+
groupVersionKind, // 现有的 apiversion,apiGroup 皆由环境配置,可以暂时不用
|
|
577
|
+
type: last(groupVersionKind.split('~')),
|
|
578
|
+
multiple,
|
|
579
|
+
validations,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
// resolve a=b&c=d 格式数据
|
|
583
|
+
function resolveKVParams(str) {
|
|
584
|
+
return (str || '')
|
|
585
|
+
.split('&')
|
|
586
|
+
.filter(i => !isEmpty(i))
|
|
587
|
+
.reduce((acc, curr) => {
|
|
588
|
+
const [k, v] = curr.split('=');
|
|
589
|
+
acc[k] = decodeURIComponent(v);
|
|
590
|
+
return acc;
|
|
591
|
+
}, {});
|
|
592
|
+
}
|
|
593
|
+
function getCapabilityDisabled(capabilities) {
|
|
594
|
+
return capabilities.includes(SpecCapability.disabled);
|
|
595
|
+
}
|
|
596
|
+
function getCapabilityValidations(capabilities) {
|
|
597
|
+
const validations = capabilities?.filter(c => c.startsWith(SpecCapability.validation));
|
|
598
|
+
return validations.reduce((acc, descriptor) => {
|
|
599
|
+
const [, validation, param] = descriptor.match(REGEXP_VALIDATION_SUFFIX) ?? [];
|
|
600
|
+
if (!validation) {
|
|
601
|
+
return acc;
|
|
602
|
+
}
|
|
603
|
+
if (validation === Validations.pattern) {
|
|
604
|
+
acc[validation] = param;
|
|
605
|
+
}
|
|
606
|
+
else if (validation === Validations.required) {
|
|
607
|
+
acc[validation] = Boolean(param);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
acc[validation] = +param;
|
|
611
|
+
}
|
|
612
|
+
return acc;
|
|
613
|
+
}, {});
|
|
614
|
+
}
|
|
615
|
+
function getOneOfMap(field) {
|
|
616
|
+
const map = {};
|
|
617
|
+
const capabilities = field.capabilities.filter(c => c.startsWith(SpecCapability.oneOf));
|
|
618
|
+
capabilities.forEach(c => {
|
|
619
|
+
const str = c.split(SpecCapability.oneOf)[1];
|
|
620
|
+
// 这里 path 与后端约束直接支持 path@tag 模式
|
|
621
|
+
const [value, ...path] = str.split(':');
|
|
622
|
+
map[value] = [...path];
|
|
623
|
+
});
|
|
624
|
+
return map;
|
|
625
|
+
}
|
|
626
|
+
function fieldShouldReveal(field) {
|
|
627
|
+
return (!field.capabilities.includes(SpecCapability.hidden) &&
|
|
628
|
+
identity(getFieldType(field)));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function getPropertyDepth(property, depth = 0) {
|
|
632
|
+
if (!property || !['object', 'array'].includes(property.type)) {
|
|
633
|
+
return depth;
|
|
634
|
+
}
|
|
635
|
+
return Math.max(0, ...Object.values(property.properties || property.items?.properties || {}).map(nestedProperty => getPropertyDepth(nestedProperty, depth + 1)));
|
|
636
|
+
}
|
|
637
|
+
function getFieldsFromDescriptor({ path, description, displayName, 'x-descriptors': capabilities = [], }, data, openApi, _originDescriptors) {
|
|
638
|
+
const { match, pathBeforeIndex, pathAfterIndex } = parseArrayPath(path);
|
|
639
|
+
const filedBasicType = getBasicCapabilityType(capabilities);
|
|
640
|
+
const disabled = getCapabilityDisabled(capabilities);
|
|
641
|
+
const validations = getCapabilityValidations(capabilities);
|
|
642
|
+
if (match) {
|
|
643
|
+
const n = get(data, toPath(`spec.${pathBeforeIndex}`), []).length || 1;
|
|
644
|
+
const pathAfterIndexStr = `.${pathAfterIndex}`;
|
|
645
|
+
let extra = [];
|
|
646
|
+
if (_originDescriptors) {
|
|
647
|
+
extra = flatten(_originDescriptors
|
|
648
|
+
.filter(d => d.path.startsWith(`${pathBeforeIndex}[0]`) && d.path !== path)
|
|
649
|
+
.map(d => times(n, index => {
|
|
650
|
+
const oPath = d.path.replace(`${pathBeforeIndex}[0]`, `${pathBeforeIndex}[${index}]`);
|
|
651
|
+
return {
|
|
652
|
+
...d,
|
|
653
|
+
path: oPath,
|
|
654
|
+
};
|
|
655
|
+
}).slice(1)));
|
|
656
|
+
}
|
|
657
|
+
return flatten([
|
|
658
|
+
...times(n, index => {
|
|
659
|
+
const propertyPath = `spec.${pathBeforeIndex}[${index}]${pathAfterIndex && pathAfterIndexStr}`;
|
|
660
|
+
const property = getOpenApiPropertyByPath(`spec.${path}`, openApi);
|
|
661
|
+
return {
|
|
662
|
+
path: propertyPath,
|
|
663
|
+
value: get(data, propertyPath),
|
|
664
|
+
description,
|
|
665
|
+
displayName,
|
|
666
|
+
capabilities,
|
|
667
|
+
type: filedBasicType || property?.type,
|
|
668
|
+
required: null,
|
|
669
|
+
disabled,
|
|
670
|
+
validations: {
|
|
671
|
+
...validations,
|
|
672
|
+
...(property?.required && { required: true }),
|
|
673
|
+
},
|
|
674
|
+
};
|
|
675
|
+
}),
|
|
676
|
+
...extra.map(e => getFieldsFromDescriptor(e, data, openApi)),
|
|
677
|
+
]);
|
|
678
|
+
}
|
|
679
|
+
// 通过 descriptor 查找原始 CRD 节点,推导类型、validation 等信息
|
|
680
|
+
const propertyPath = `spec.${path}`;
|
|
681
|
+
const property = getOpenApiPropertyByPath(propertyPath, openApi);
|
|
682
|
+
return [
|
|
683
|
+
{
|
|
684
|
+
path: propertyPath,
|
|
685
|
+
description,
|
|
686
|
+
displayName,
|
|
687
|
+
capabilities,
|
|
688
|
+
disabled,
|
|
689
|
+
value: get(data, propertyPath),
|
|
690
|
+
type: filedBasicType || property?.type,
|
|
691
|
+
required: null,
|
|
692
|
+
validations: {
|
|
693
|
+
...validations,
|
|
694
|
+
...(property?.required && { required: true }),
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
];
|
|
698
|
+
}
|
|
699
|
+
function getOpenApiPropertyByPath(descriptorPath, openApi) {
|
|
700
|
+
const paths = descriptorPath.split('.');
|
|
701
|
+
try {
|
|
702
|
+
return paths.reduce((acc, path) => {
|
|
703
|
+
acc = path.includes('[0]')
|
|
704
|
+
? acc.items.properties[path.split('[0]')[0]]
|
|
705
|
+
: acc.properties[path];
|
|
706
|
+
return acc;
|
|
707
|
+
}, openApi);
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function getFieldsFromOpenApiSchema(openApi, descriptors, data, openApiSchemaPath = SCHEMA_PATH, maxDepth = MAX_DEPTH) {
|
|
714
|
+
const path = isArray(openApiSchemaPath)
|
|
715
|
+
? openApiSchemaPath
|
|
716
|
+
: openApiSchemaPath.split('.').filter(Boolean);
|
|
717
|
+
const properties = get(openApi, [...path, 'properties'], {});
|
|
718
|
+
const required = get(openApi, [...path, 'required'], []);
|
|
719
|
+
return reduce(properties, (accumulator, _property, propertyName) => {
|
|
720
|
+
const property = _property;
|
|
721
|
+
if (!property.type || getPropertyDepth(property) > maxDepth) {
|
|
722
|
+
return accumulator;
|
|
723
|
+
}
|
|
724
|
+
return [
|
|
725
|
+
...accumulator,
|
|
726
|
+
...flattenNestedProperties(propertyName, property, descriptors, data, {
|
|
727
|
+
required: required.includes(propertyName),
|
|
728
|
+
}),
|
|
729
|
+
];
|
|
730
|
+
}, []);
|
|
731
|
+
}
|
|
732
|
+
function flattenNestedProperties(propertyName, property, descriptors, data, { currentCapabilities = [], currentPath = [], required = false, }) {
|
|
733
|
+
if (!property) {
|
|
734
|
+
return [];
|
|
735
|
+
}
|
|
736
|
+
const handleObjectProperty = () => flatMap(property.properties, (nestedProperty, nestedPropertyName) => flattenNestedProperties(nestedPropertyName, nestedProperty, descriptors, data, {
|
|
737
|
+
currentCapabilities: [
|
|
738
|
+
...currentCapabilities,
|
|
739
|
+
`${SpecCapability.fieldGroup}${propertyName}`,
|
|
740
|
+
],
|
|
741
|
+
currentPath: [...currentPath, propertyName],
|
|
742
|
+
required: (property.required || []).includes(nestedPropertyName),
|
|
743
|
+
}));
|
|
744
|
+
const handleArrayProperty = () => {
|
|
745
|
+
// Find the number of array elements that are already defined in the provided object
|
|
746
|
+
const n = get(data, `spec.${currentPath.join('.')}${propertyName}`, []).length || 1;
|
|
747
|
+
// Since _.times will return a multidimensional array of OperandFields (OperandField[][]), we
|
|
748
|
+
// need to flatten one level deeper than _.flatMap provides.
|
|
749
|
+
return flatMapDepth(property.items.properties, (nestedProperty, nestedPropertyName) =>
|
|
750
|
+
// Repeat recursion (n) times so that the correct number of fields are created for
|
|
751
|
+
// existing values in obj. This ensures that further nested fields also get created.
|
|
752
|
+
times(n, index => flattenNestedProperties(nestedPropertyName, nestedProperty, descriptors, data, {
|
|
753
|
+
currentCapabilities: [
|
|
754
|
+
...currentCapabilities,
|
|
755
|
+
`${SpecCapability.arrayFieldGroup}${propertyName}`,
|
|
756
|
+
],
|
|
757
|
+
currentPath: [...currentPath, `${propertyName}[${index}]`], // Array field paths must include an index
|
|
758
|
+
required: (property?.required || []).includes(nestedPropertyName),
|
|
759
|
+
})), 2);
|
|
760
|
+
};
|
|
761
|
+
const handleAtomicProperty = () => {
|
|
762
|
+
const path = [...currentPath, propertyName].join('.');
|
|
763
|
+
const descriptor = descriptors.find(item => item.path === modifyArrayFieldPathIndex(path, () => 0));
|
|
764
|
+
const capabilities = union(descriptor?.[DESCRIPTOR_DEFINITION_KEY] || [], currentCapabilities, getCapabilitiesForOpenApiProperty(property, descriptor?.[DESCRIPTOR_DEFINITION_KEY]));
|
|
765
|
+
const validations = getCapabilityValidations(capabilities);
|
|
766
|
+
const fieldPath = `spec.${path}`;
|
|
767
|
+
return [
|
|
768
|
+
{
|
|
769
|
+
path: fieldPath,
|
|
770
|
+
type: property.type,
|
|
771
|
+
required,
|
|
772
|
+
capabilities,
|
|
773
|
+
description: descriptor?.description || property.description,
|
|
774
|
+
displayName: descriptor?.displayName || startCase(propertyName),
|
|
775
|
+
validations: {
|
|
776
|
+
...pick(property, Object.keys(Validations)),
|
|
777
|
+
...validations,
|
|
778
|
+
},
|
|
779
|
+
value: get(data, fieldPath),
|
|
780
|
+
},
|
|
781
|
+
];
|
|
782
|
+
};
|
|
783
|
+
switch (property.type) {
|
|
784
|
+
case 'object': {
|
|
785
|
+
return handleObjectProperty();
|
|
786
|
+
}
|
|
787
|
+
case 'array': {
|
|
788
|
+
return handleArrayProperty();
|
|
789
|
+
}
|
|
790
|
+
default: {
|
|
791
|
+
return handleAtomicProperty();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// Accepts an OpenAPI spec property and returns a corresponding SpecCapability[] array.
|
|
796
|
+
function getCapabilitiesForOpenApiProperty(property, capabilities = []) {
|
|
797
|
+
const formControlCapabilities = [
|
|
798
|
+
SpecCapabilityUIPrefix,
|
|
799
|
+
SpecCapabilityWidgetsPrefix,
|
|
800
|
+
SpecCapability.k8sResourcePrefix,
|
|
801
|
+
];
|
|
802
|
+
if (capabilities.some(i => formControlCapabilities.some(k => i.startsWith(k)))) {
|
|
803
|
+
return [];
|
|
804
|
+
}
|
|
805
|
+
if (property.enum) {
|
|
806
|
+
return (property.enum || []).map((option) => `${SpecCapability.select}${option}`);
|
|
807
|
+
}
|
|
808
|
+
switch (property.type) {
|
|
809
|
+
case 'integer': {
|
|
810
|
+
return [SpecCapability.number];
|
|
811
|
+
}
|
|
812
|
+
case 'boolean': {
|
|
813
|
+
return [SpecCapability.booleanSwitch];
|
|
814
|
+
}
|
|
815
|
+
default: {
|
|
816
|
+
return [SpecCapability.text];
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function modifyArrayFieldPathIndex(path, operation) {
|
|
821
|
+
const { match, index, pathBeforeIndex, pathAfterIndex } = parseArrayPath(path);
|
|
822
|
+
const pathAfterIndexStr = `.${pathAfterIndex}`;
|
|
823
|
+
return match
|
|
824
|
+
? `${pathBeforeIndex}[${operation(index)}]${pathAfterIndex && pathAfterIndexStr}`
|
|
825
|
+
: path;
|
|
826
|
+
}
|
|
827
|
+
function pathToArray(path) {
|
|
828
|
+
return map(toPath(path), subPath => /^\d+$/.test(subPath) ? parseInt$1(subPath, 10) : subPath);
|
|
829
|
+
}
|
|
830
|
+
function parseGroupDescriptor(field) {
|
|
831
|
+
const groupDescriptor = find(field.capabilities, descriptor => descriptor.startsWith(SpecCapability.fieldGroup) ||
|
|
832
|
+
descriptor.startsWith(SpecCapability.arrayFieldGroup));
|
|
833
|
+
const [match, groupType, groupName] = GROUP_PATTERN.exec(groupDescriptor) || [];
|
|
834
|
+
return { match, groupName, groupType };
|
|
835
|
+
}
|
|
836
|
+
function normalizePath(path) {
|
|
837
|
+
return path?.startsWith('spec.') ? path : `spec.${path}`;
|
|
838
|
+
}
|
|
839
|
+
function getFormData(formData, path, enableConvert = true) {
|
|
840
|
+
const value = formData.getIn(pathToArray(normalizePath(path)));
|
|
841
|
+
return enableConvert && value == null ? null : value;
|
|
842
|
+
}
|
|
843
|
+
function createCapabilityField(descriptors = [], openApiSchema, data, openApiSchemaPath) {
|
|
844
|
+
const openApiFields = getFieldsFromOpenApiSchema(openApiSchema, descriptors, data, openApiSchemaPath);
|
|
845
|
+
const descriptorFields = uniqWith(reduce(descriptors, (accumulator, descriptor) => openApiFields?.some(field => field.path === `spec.${descriptor.path}`)
|
|
846
|
+
? accumulator
|
|
847
|
+
: [
|
|
848
|
+
...accumulator,
|
|
849
|
+
...getFieldsFromDescriptor(descriptor, data, openApiSchema, descriptors),
|
|
850
|
+
], []), isEqual);
|
|
851
|
+
const basicFields = [...openApiFields, ...descriptorFields];
|
|
852
|
+
const relationFields = getRelatedFields(basicFields.filter(fieldShouldReveal));
|
|
853
|
+
const treeFields = [
|
|
854
|
+
...getTreeFields(relationFields.filter(f => !f.parent), relationFields.filter(f => !!f.parent)),
|
|
855
|
+
// 兼容老的数据结构
|
|
856
|
+
...relationFields.filter(f => !f._dirty),
|
|
857
|
+
];
|
|
858
|
+
// 保留,兼容老数据结构
|
|
859
|
+
const [advancedFields = [], arrayFields = [], groupFields = [], normalFields = [],] = reduce(treeFields, ([advancedFieldsAccumulator = [], arrayFieldsAccumulator = [], groupFieldsAccumulator = [], normalFieldsAccumulator = [],], field) => {
|
|
860
|
+
const mappers = [
|
|
861
|
+
{ spec: SpecCapability.arrayFieldGroup, acc: arrayFieldsAccumulator },
|
|
862
|
+
{ spec: SpecCapability.fieldGroup, acc: groupFieldsAccumulator },
|
|
863
|
+
{ spec: SpecCapability.advanced, acc: advancedFieldsAccumulator },
|
|
864
|
+
];
|
|
865
|
+
const acc = mappers.find(mapper => hasDescriptor(field, mapper.spec))?.acc;
|
|
866
|
+
return [
|
|
867
|
+
advancedFieldsAccumulator,
|
|
868
|
+
arrayFieldsAccumulator,
|
|
869
|
+
groupFieldsAccumulator,
|
|
870
|
+
normalFieldsAccumulator,
|
|
871
|
+
].map(r => {
|
|
872
|
+
if ((!acc && r === normalFieldsAccumulator) || r === acc) {
|
|
873
|
+
return [...r, field];
|
|
874
|
+
}
|
|
875
|
+
return r;
|
|
876
|
+
});
|
|
877
|
+
}, []);
|
|
878
|
+
return {
|
|
879
|
+
normalFields: sortFields(normalFields, descriptors),
|
|
880
|
+
fieldGroups: sortGroupFields(groupFields, descriptors, descriptorFields),
|
|
881
|
+
arrayFieldGroups: getArrayFields(arrayFields, descriptorFields),
|
|
882
|
+
advancedFields: sortFields(advancedFields, descriptors),
|
|
883
|
+
descriptorFields,
|
|
884
|
+
openApiFields,
|
|
885
|
+
basicFields,
|
|
886
|
+
treeFields,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function getArrayFields(fields, descriptorFields) {
|
|
890
|
+
// array fields
|
|
891
|
+
const groupedArrayFieldsByName = groupBy(fields, field => {
|
|
892
|
+
const { groupName } = parseGroupDescriptor(field);
|
|
893
|
+
return groupName;
|
|
894
|
+
});
|
|
895
|
+
// Map {groupName: string, fieldLists: OperandField[][]}, where OperandField is a nested array
|
|
896
|
+
// of the appropriate fields, grouped by index.
|
|
897
|
+
return map(groupedArrayFieldsByName, (fieldsInGroup, groupName) => ({
|
|
898
|
+
groupName,
|
|
899
|
+
displayName: startCase(groupName),
|
|
900
|
+
capabilities: descriptorFields.find(field => field.path === `spec.${groupName}`)
|
|
901
|
+
?.capabilities ?? [],
|
|
902
|
+
fieldLists: reduce(fieldsInGroup, (fieldListsAccumulator, field) => {
|
|
903
|
+
const { index, match } = parseArrayPath(field.path);
|
|
904
|
+
if (match) {
|
|
905
|
+
fieldListsAccumulator[index] = [
|
|
906
|
+
...(fieldListsAccumulator[index] || []),
|
|
907
|
+
field,
|
|
908
|
+
];
|
|
909
|
+
}
|
|
910
|
+
return fieldListsAccumulator;
|
|
911
|
+
}, []),
|
|
912
|
+
_dirty: false,
|
|
913
|
+
}));
|
|
914
|
+
}
|
|
915
|
+
function sortFields(fields, descriptors) {
|
|
916
|
+
return sortBy(fields, field => descriptors.findIndex(d => normalizePath(d.path) === normalizePath(field.path)));
|
|
917
|
+
}
|
|
918
|
+
function sortGroupFields(fields, descriptors, descriptorFields) {
|
|
919
|
+
const groupedFieldsByName = groupBy(sortFields(fields, descriptors), field => {
|
|
920
|
+
const { groupName } = parseGroupDescriptor(field);
|
|
921
|
+
return groupName;
|
|
922
|
+
});
|
|
923
|
+
return map(groupedFieldsByName, (fieldList, groupName) => ({
|
|
924
|
+
groupName,
|
|
925
|
+
displayName: startCase(groupName),
|
|
926
|
+
capabilities: descriptorFields.find(field => field.path === `spec.${groupName}`)
|
|
927
|
+
?.capabilities ?? [],
|
|
928
|
+
fieldList,
|
|
929
|
+
}));
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const EXPRESSION_PROPS_PREFIX = `${SpecCapability.expression}props.`;
|
|
933
|
+
const EXPRESSION_PROPS_OPTIONS_PREFIX = `${EXPRESSION_PROPS_PREFIX}options:`;
|
|
934
|
+
const EXPRESSION_PROPS_DEFAULT_PREFIX = `${EXPRESSION_PROPS_PREFIX}default:`;
|
|
935
|
+
const EXPRESSION_PROPS_HIDDEN_PREFIX = `${EXPRESSION_PROPS_PREFIX}hidden:`;
|
|
936
|
+
const EXPRESSION_PROPS_ALLOW_CREATE_PREFIX = `${EXPRESSION_PROPS_PREFIX}allowCreate:`;
|
|
937
|
+
class PropsFieldExpression {
|
|
938
|
+
constructor(field) {
|
|
939
|
+
this.expressions = this.evaluatePropsExpressions(field);
|
|
940
|
+
}
|
|
941
|
+
evaluatePropsExpressions(field) {
|
|
942
|
+
return {
|
|
943
|
+
options: this.evaluateOptionsExpression(field),
|
|
944
|
+
default: this.evaluateCommonPropsExpression(field, EXPRESSION_PROPS_DEFAULT_PREFIX),
|
|
945
|
+
hidden: this.evaluateCommonPropsExpression(field, EXPRESSION_PROPS_HIDDEN_PREFIX),
|
|
946
|
+
subscribedContextPaths: this.evaluateSubscribedContextPaths(field),
|
|
947
|
+
subscribedFormDataPaths: this.evaluateSubscribedFormDataPaths(field),
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
evaluateOptionsExpression(field) {
|
|
951
|
+
const propsVisible = field.capabilities.some(c => c.startsWith(EXPRESSION_PROPS_OPTIONS_PREFIX));
|
|
952
|
+
return propsVisible
|
|
953
|
+
? {
|
|
954
|
+
api: getMatchedCapabilityValue(field.capabilities, `${EXPRESSION_PROPS_OPTIONS_PREFIX}api:`),
|
|
955
|
+
labelPath: getMatchedCapabilityValue(field.capabilities, `${EXPRESSION_PROPS_OPTIONS_PREFIX}label:path:`),
|
|
956
|
+
valuePath: getMatchedCapabilityValue(field.capabilities, `${EXPRESSION_PROPS_OPTIONS_PREFIX}value:path:`),
|
|
957
|
+
expression: getMatchedCapabilityValue(field.capabilities, `${EXPRESSION_PROPS_OPTIONS_PREFIX}`),
|
|
958
|
+
}
|
|
959
|
+
: null;
|
|
960
|
+
}
|
|
961
|
+
evaluateCommonPropsExpression(field, prefix) {
|
|
962
|
+
return getMatchedCapabilityValue(field.capabilities, prefix);
|
|
963
|
+
}
|
|
964
|
+
evaluateSubscribedContextPaths(field) {
|
|
965
|
+
return field.capabilities.reduce((p, c) => {
|
|
966
|
+
const matched = c.match(/(?<=\${context.)[^\t\n\v\f\r{}]+(?=})/g) || [];
|
|
967
|
+
return [...p, ...matched];
|
|
968
|
+
}, []);
|
|
969
|
+
}
|
|
970
|
+
evaluateSubscribedFormDataPaths(field) {
|
|
971
|
+
return field.capabilities.reduce((p, c) => {
|
|
972
|
+
const matched = c.match(/(?<=\${formData.)[^\t\n\v\f\r{}]+(?=})/g) || [];
|
|
973
|
+
return [...p, ...matched];
|
|
974
|
+
}, []);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const compareWithSubscribedPaths = (dynamicExpression, prev, next) => {
|
|
978
|
+
const [prevFormContext, prevFormDataState] = prev;
|
|
979
|
+
const [nextFormContext, nextFormDataState] = next;
|
|
980
|
+
return (!dynamicExpression.subscribedContextPaths.some(path => !isEqual(get(prevFormContext, path), get(nextFormContext, path))) &&
|
|
981
|
+
!dynamicExpression.subscribedFormDataPaths.some(path => !isEqual(getFormData(prevFormDataState, path), getFormData(nextFormDataState, path))));
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
class FormItemComponent {
|
|
985
|
+
constructor() {
|
|
986
|
+
this.getFieldTooltip = getFieldTooltip;
|
|
987
|
+
this.getFieldDescription = getFieldDescription$1;
|
|
988
|
+
this.getFieldDocLink = getFieldDocLink;
|
|
989
|
+
this.isFieldDescriptionAsLink = isFieldDescriptionAsLink;
|
|
990
|
+
this.fieldComponent = inject(forwardRef(() => OperandFieldComponent));
|
|
991
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
992
|
+
// FIXME: 部分页面需要调用 markForCheck 才行, 需要确认为啥使用了 ChangeDetectionStrategy.Default 数据变更之后视图依旧不同步更新
|
|
993
|
+
this.fieldComponent.crdForm.form.ngSubmit.subscribe(() => this.cdr.markForCheck());
|
|
994
|
+
}
|
|
995
|
+
showFieldCopyable(field, isDisabled) {
|
|
996
|
+
return isFieldCopyable(field) && isDisabled;
|
|
997
|
+
}
|
|
998
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: FormItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
999
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: FormItemComponent, isStandalone: true, selector: "acl-crd-form-item", inputs: { plain: "plain" }, ngImport: i0, template: "@if (\n {\n label: fieldComponent.field | pure: fieldComponent.getFieldDisplayName,\n };\n as _\n) {\n <aui-form-item\n [emptyAddon]=\"false\"\n [plain]=\"plain\"\n [attr.data-test]=\"_.label ? 'AUI-FORM-ITEM/' + (_.label | translate) : null\"\n width=\"large\"\n >\n @if (_.label) {\n <label auiFormItemLabel>{{ _.label | translate }}</label>\n }\n <!-- for a required mark in aui-form-item label -->\n @if (!fieldComponent.isDisabled) {\n <ng-container\n auiFormItemControl\n [required]=\"fieldComponent.isRequired\"\n ></ng-container>\n }\n <ng-content></ng-content>\n @if (!fieldComponent.isDisabled) {\n <div auiFormItemHint>\n @if (\n (fieldComponent.crdForm.form.submitted ||\n fieldComponent.control.dirty) &&\n fieldComponent.control.invalid &&\n !fieldComponent.isCustomErrorsMapper\n ) {\n <acl-errors-mapper [errors]=\"fieldComponent.control.errors\">\n </acl-errors-mapper>\n }\n <div>\n @if (!(fieldComponent.field | pure: isFieldDescriptionAsLink)) {\n {{ fieldComponent.field | pure: getFieldDescription | translate }}\n }\n @if (fieldComponent.field | pure: getFieldDocLink; as link) {\n <a\n href=\"javascript:;\"\n [aclHelpDoc]=\"link\"\n aclHelpDocTarget=\"blank\"\n >\n @if (fieldComponent.field | pure: isFieldDescriptionAsLink) {\n {{\n fieldComponent.field | pure: getFieldDescription | translate\n }}\n }\n <aui-icon icon=\"jump\"></aui-icon>\n </a>\n }\n </div>\n </div>\n }\n @if (\n (fieldComponent.field | pure: getFieldTooltip) ||\n (fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled)\n ) {\n <div auiFormItemAddon>\n @if (\n fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled\n ) {\n <aui-icon\n icon=\"copy\"\n [auiTooltipCopy]=\"fieldComponent.control.value\"\n ></aui-icon>\n }\n @if (fieldComponent.field | pure: getFieldTooltip; as tooltip) {\n <aui-icon\n icon=\"question_circle\"\n [class]=\"\n (fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled)\n ? 'tw-ml-8'\n : ''\n \"\n [auiTooltip]=\"tooltipTmp\"\n ></aui-icon>\n <ng-template #tooltipTmp>\n <span style=\"white-space: pre-line\">\n {{ tooltip | translate }}</span\n >\n </ng-template>\n }\n </div>\n }\n </aui-form-item>\n}\n", styles: [":host::ng-deep label{word-break:break-word}aui-form-item ::ng-deep .aui-form-item__content--large aui-switch.aui-form-item__control{flex:unset}\n"], dependencies: [{ kind: "ngmodule", type: FormModule }, { kind: "component", type: i2.FormItemComponent, selector: "aui-form-item", inputs: ["labelWidth", "width", "labelPosition", "emptyAddon", "plain"] }, { kind: "directive", type: i2.FormItemAddonDirective, selector: "[auiFormItemAddon]" }, { kind: "directive", type: i2.FormItemHintDirective, selector: "[auiFormItemHint]" }, { kind: "directive", type: i2.FormItemLabelDirective, selector: "label[auiFormItemLabel]" }, { kind: "directive", type: i2.FormItemControlDirective, selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "ngmodule", type: IconModule }, { kind: "component", type: i2.IconComponent, selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2.TooltipDirective, selector: "[auiTooltip]", inputs: ["auiTooltip", "auiTooltipContext", "auiTooltipClass", "auiTooltipType", "auiTooltipPosition", "auiTooltipTrigger", "auiTooltipDisabled", "auiTooltipHideOnClick", "auiTooltipAnimType"], outputs: ["auiTooltipVisibleChange"], exportAs: ["auiTooltip"] }, { kind: "directive", type: i2.TooltipCopyDirective, selector: "[auiTooltipCopy]", inputs: ["auiTooltipPosition", "auiTooltipDisabled", "auiTooltipCopy", "auiTooltipCopyTip", "auiTooltipCopySuccessTip", "auiTooltipCopyFailTip"] }, { kind: "component", type: ErrorsMapperComponent, selector: "acl-errors-mapper", inputs: ["errors", "errorsMapper", "errorsMapperFn", "controlName", "ignoreUnknownError"] }, { kind: "directive", type: HelpDocDirective, selector: "[aclHelpDoc]", inputs: ["aclHelpDoc", "aclHelpDocTarget"] }, { kind: "pipe", type: PurePipe, name: "pure" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
|
|
1000
|
+
}
|
|
1001
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: FormItemComponent, decorators: [{
|
|
1002
|
+
type: Component,
|
|
1003
|
+
args: [{ selector: 'acl-crd-form-item', changeDetection: ChangeDetectionStrategy.Default, standalone: true, imports: [
|
|
1004
|
+
PurePipe,
|
|
1005
|
+
TranslatePipe,
|
|
1006
|
+
FormModule,
|
|
1007
|
+
IconModule,
|
|
1008
|
+
TooltipModule,
|
|
1009
|
+
ErrorsMapperComponent,
|
|
1010
|
+
HelpDocDirective,
|
|
1011
|
+
], template: "@if (\n {\n label: fieldComponent.field | pure: fieldComponent.getFieldDisplayName,\n };\n as _\n) {\n <aui-form-item\n [emptyAddon]=\"false\"\n [plain]=\"plain\"\n [attr.data-test]=\"_.label ? 'AUI-FORM-ITEM/' + (_.label | translate) : null\"\n width=\"large\"\n >\n @if (_.label) {\n <label auiFormItemLabel>{{ _.label | translate }}</label>\n }\n <!-- for a required mark in aui-form-item label -->\n @if (!fieldComponent.isDisabled) {\n <ng-container\n auiFormItemControl\n [required]=\"fieldComponent.isRequired\"\n ></ng-container>\n }\n <ng-content></ng-content>\n @if (!fieldComponent.isDisabled) {\n <div auiFormItemHint>\n @if (\n (fieldComponent.crdForm.form.submitted ||\n fieldComponent.control.dirty) &&\n fieldComponent.control.invalid &&\n !fieldComponent.isCustomErrorsMapper\n ) {\n <acl-errors-mapper [errors]=\"fieldComponent.control.errors\">\n </acl-errors-mapper>\n }\n <div>\n @if (!(fieldComponent.field | pure: isFieldDescriptionAsLink)) {\n {{ fieldComponent.field | pure: getFieldDescription | translate }}\n }\n @if (fieldComponent.field | pure: getFieldDocLink; as link) {\n <a\n href=\"javascript:;\"\n [aclHelpDoc]=\"link\"\n aclHelpDocTarget=\"blank\"\n >\n @if (fieldComponent.field | pure: isFieldDescriptionAsLink) {\n {{\n fieldComponent.field | pure: getFieldDescription | translate\n }}\n }\n <aui-icon icon=\"jump\"></aui-icon>\n </a>\n }\n </div>\n </div>\n }\n @if (\n (fieldComponent.field | pure: getFieldTooltip) ||\n (fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled)\n ) {\n <div auiFormItemAddon>\n @if (\n fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled\n ) {\n <aui-icon\n icon=\"copy\"\n [auiTooltipCopy]=\"fieldComponent.control.value\"\n ></aui-icon>\n }\n @if (fieldComponent.field | pure: getFieldTooltip; as tooltip) {\n <aui-icon\n icon=\"question_circle\"\n [class]=\"\n (fieldComponent.field\n | pure: showFieldCopyable : fieldComponent.isDisabled)\n ? 'tw-ml-8'\n : ''\n \"\n [auiTooltip]=\"tooltipTmp\"\n ></aui-icon>\n <ng-template #tooltipTmp>\n <span style=\"white-space: pre-line\">\n {{ tooltip | translate }}</span\n >\n </ng-template>\n }\n </div>\n }\n </aui-form-item>\n}\n", styles: [":host::ng-deep label{word-break:break-word}aui-form-item ::ng-deep .aui-form-item__content--large aui-switch.aui-form-item__control{flex:unset}\n"] }]
|
|
1012
|
+
}], ctorParameters: () => [], propDecorators: { plain: [{
|
|
1013
|
+
type: Input
|
|
1014
|
+
}] } });
|
|
1015
|
+
|
|
1016
|
+
class OperandArrayFieldGroupComponent extends BaseOperandFiledArrayComponent {
|
|
1017
|
+
ngOnInit() {
|
|
1018
|
+
// 如果 arrayFieldGroup 配置了 urn:alm:descriptor:com.tectonic.ui:emptyArray 并且没有 value 值,则不保留首项
|
|
1019
|
+
if (!get(this.arrayFieldGroup.value, 'length') &&
|
|
1020
|
+
isEmptyArray(this.arrayFieldGroup)) {
|
|
1021
|
+
this.removeItem(0);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandArrayFieldGroupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1025
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: OperandArrayFieldGroupComponent, isStandalone: true, selector: "acl-operand-array-field-group", usesInheritance: true, ngImport: i0, template: "<div\n [class.expanded]=\"expanded\"\n class=\"field-group\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n @if (expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n <span>{{ arrayFieldGroup | pure: getFieldDisplayName | translate }}</span>\n </div>\n <div class=\"description\"></div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (list of arrayFieldGroup.fieldLists; track list; let i = $index) {\n <div class=\"array-item\">\n @if (!readonly) {\n <div class=\"array-item__remove\">\n <button\n aui-button=\"text\"\n size=\"small\"\n (click)=\"removeItem(i)\"\n >\n <aui-icon icon=\"minus_circle\"></aui-icon>\n <span>{{ 'remove' | translate }}</span>\n </button>\n </div>\n }\n @for (field of list; track field) {\n <div>\n <acl-operand-field\n [field]=\"field\"\n [fields]=\"fields\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n </div>\n }\n </div>\n }\n @if (!readonly) {\n <div class=\"array-item__add\">\n <button\n aui-button=\"text\"\n size=\"small\"\n (click)=\"addItem()\"\n >\n <aui-icon icon=\"plus_circle\"></aui-icon>\n <span\n >{{ 'add' | translate }}\n {{ arrayFieldGroup | pure: getFieldDisplayName | translate }}</span\n >\n </button>\n </div>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => IconModule) }, { kind: "component", type: i0.forwardRef(() => i2.IconComponent), selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "ngmodule", type: i0.forwardRef(() => ButtonModule) }, { kind: "component", type: i0.forwardRef(() => i2.ButtonComponent), selector: "button[aui-button]", inputs: ["aui-button", "size", "plain", "loading", "round", "square"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldComponent), selector: "acl-operand-field", inputs: ["field", "readonly", "fields", "formDataState", "formErrors"], outputs: ["valueChange", "itemRemove"] }, { kind: "pipe", type: i0.forwardRef(() => PurePipe), name: "pure" }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1026
|
+
}
|
|
1027
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandArrayFieldGroupComponent, decorators: [{
|
|
1028
|
+
type: Component,
|
|
1029
|
+
args: [{ selector: 'acl-operand-array-field-group', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1030
|
+
PurePipe,
|
|
1031
|
+
TranslatePipe,
|
|
1032
|
+
IconModule,
|
|
1033
|
+
ButtonModule,
|
|
1034
|
+
forwardRef(() => OperandFieldComponent),
|
|
1035
|
+
], template: "<div\n [class.expanded]=\"expanded\"\n class=\"field-group\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n @if (expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n <span>{{ arrayFieldGroup | pure: getFieldDisplayName | translate }}</span>\n </div>\n <div class=\"description\"></div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (list of arrayFieldGroup.fieldLists; track list; let i = $index) {\n <div class=\"array-item\">\n @if (!readonly) {\n <div class=\"array-item__remove\">\n <button\n aui-button=\"text\"\n size=\"small\"\n (click)=\"removeItem(i)\"\n >\n <aui-icon icon=\"minus_circle\"></aui-icon>\n <span>{{ 'remove' | translate }}</span>\n </button>\n </div>\n }\n @for (field of list; track field) {\n <div>\n <acl-operand-field\n [field]=\"field\"\n [fields]=\"fields\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n </div>\n }\n </div>\n }\n @if (!readonly) {\n <div class=\"array-item__add\">\n <button\n aui-button=\"text\"\n size=\"small\"\n (click)=\"addItem()\"\n >\n <aui-icon icon=\"plus_circle\"></aui-icon>\n <span\n >{{ 'add' | translate }}\n {{ arrayFieldGroup | pure: getFieldDisplayName | translate }}</span\n >\n </button>\n </div>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"] }]
|
|
1036
|
+
}] });
|
|
1037
|
+
|
|
1038
|
+
class OperandFieldGroupComponent {
|
|
1039
|
+
constructor() {
|
|
1040
|
+
this.readonly = false;
|
|
1041
|
+
this.valueChange = new EventEmitter();
|
|
1042
|
+
}
|
|
1043
|
+
get expanded() {
|
|
1044
|
+
return this._expanded ?? !isFolded(this.fieldGroup);
|
|
1045
|
+
}
|
|
1046
|
+
set expanded(expanded) {
|
|
1047
|
+
this._expanded = expanded;
|
|
1048
|
+
}
|
|
1049
|
+
fieldValueChange(e) {
|
|
1050
|
+
this.valueChange.emit(e);
|
|
1051
|
+
}
|
|
1052
|
+
getGroupName(field) {
|
|
1053
|
+
return getFieldDisplayName(field) || field.groupName;
|
|
1054
|
+
}
|
|
1055
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandFieldGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1056
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: OperandFieldGroupComponent, isStandalone: true, selector: "acl-operand-field-group", inputs: { fields: "fields", fieldGroup: "fieldGroup", formDataState: "formDataState", formErrors: "formErrors", readonly: "readonly" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div\n class=\"field-group\"\n [class.expanded]=\"expanded\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon icon=\"caret_down_s\"></aui-icon>\n }\n @if (expanded) {\n <aui-icon icon=\"caret_down_s\"></aui-icon>\n }\n <span>{{ fieldGroup | pure: getGroupName | translate }}</span>\n </div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (field of fieldGroup.fieldList; track field) {\n <acl-operand-field\n [field]=\"$any(field)\"\n [fields]=\"fields\"\n [readonly]=\"readonly\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => IconModule) }, { kind: "component", type: i0.forwardRef(() => i2.IconComponent), selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldComponent), selector: "acl-operand-field", inputs: ["field", "readonly", "fields", "formDataState", "formErrors"], outputs: ["valueChange", "itemRemove"] }, { kind: "pipe", type: i0.forwardRef(() => PurePipe), name: "pure" }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1057
|
+
}
|
|
1058
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandFieldGroupComponent, decorators: [{
|
|
1059
|
+
type: Component,
|
|
1060
|
+
args: [{ selector: 'acl-operand-field-group', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1061
|
+
PurePipe,
|
|
1062
|
+
TranslatePipe,
|
|
1063
|
+
IconModule,
|
|
1064
|
+
forwardRef(() => OperandFieldComponent),
|
|
1065
|
+
], template: "<div\n class=\"field-group\"\n [class.expanded]=\"expanded\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon icon=\"caret_down_s\"></aui-icon>\n }\n @if (expanded) {\n <aui-icon icon=\"caret_down_s\"></aui-icon>\n }\n <span>{{ fieldGroup | pure: getGroupName | translate }}</span>\n </div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (field of fieldGroup.fieldList; track field) {\n <acl-operand-field\n [field]=\"$any(field)\"\n [fields]=\"fields\"\n [readonly]=\"readonly\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"] }]
|
|
1066
|
+
}], propDecorators: { fields: [{
|
|
1067
|
+
type: Input
|
|
1068
|
+
}], fieldGroup: [{
|
|
1069
|
+
type: Input
|
|
1070
|
+
}], formDataState: [{
|
|
1071
|
+
type: Input
|
|
1072
|
+
}], formErrors: [{
|
|
1073
|
+
type: Input
|
|
1074
|
+
}], readonly: [{
|
|
1075
|
+
type: Input
|
|
1076
|
+
}], valueChange: [{
|
|
1077
|
+
type: Output
|
|
1078
|
+
}] } });
|
|
1079
|
+
|
|
1080
|
+
class CrdFormArrayTableComponent extends BaseOperandFiledArrayComponent {
|
|
1081
|
+
ngOnInit() {
|
|
1082
|
+
// arrayField group 不需要默认保留一项
|
|
1083
|
+
if (!get(this.arrayFieldGroup.value, 'length')) {
|
|
1084
|
+
this.removeItem(0);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CrdFormArrayTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1088
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: CrdFormArrayTableComponent, isStandalone: true, selector: "acl-crd-array-table", usesInheritance: true, ngImport: i0, template: "<aui-form-item [emptyAddon]=\"false\">\n <label auiFormItemLabel>{{\n arrayFieldGroup | pure: getFieldDisplayName | translate\n }}</label>\n\n <acl-array-form-table\n style=\"flex: 1\"\n (remove)=\"removeItem($event)\"\n (add)=\"addItem()\"\n [rows]=\"arrayFieldGroup.fieldLists\"\n [readonly]=\"readonly\"\n class=\"form-table__flex-layout\"\n [resourceNameTranslated]=\"\n arrayFieldGroup | pure: getFieldDisplayName | translate\n \"\n >\n <ng-container *aclArrayFormTableHeader>\n @for (field of basicArrayFieldTemplate; track field) {\n @if (field | pure: fieldIsRequired) {\n <th required>\n {{ field | pure: getFieldDisplayName | translate }}\n </th>\n } @else {\n <th>{{ field | pure: getFieldDisplayName | translate }}</th>\n }\n }\n </ng-container>\n\n <!-- Input row -->\n <ng-container *aclArrayFormTableRow=\"let fieldList; let index = index\">\n @for (field of fieldList; track trackByFn(index); let index = $index) {\n <td>\n <acl-operand-field\n [field]=\"field\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n </td>\n }\n </ng-container>\n </acl-array-form-table>\n</aui-form-item>\n", styles: [":host{flex:1}:host ::ng-deep acl-array-form-table .acl-array-form-table__readonly-mode tbody tr{height:auto}:host ::ng-deep acl-array-form-table .aui-form-item{margin-bottom:0}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__label-wrapper{display:none}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__content{min-height:auto}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__content .aui-form-item__control{max-width:100%}:host ::ng-deep acl-array-form-table .acl-array-form-table tbody td{vertical-align:top}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => FormModule) }, { kind: "component", type: i0.forwardRef(() => i2.FormItemComponent), selector: "aui-form-item", inputs: ["labelWidth", "width", "labelPosition", "emptyAddon", "plain"] }, { kind: "directive", type: i0.forwardRef(() => i2.FormItemLabelDirective), selector: "label[auiFormItemLabel]" }, { kind: "component", type: i0.forwardRef(() => i2$1.ArrayFormTableComponent), selector: "acl-array-form-table", inputs: ["rowSeparator", "rows", "resourceName", "resourceNameTranslated", "readonly", "addDisabled", "actionColumnDivider", "showZeroState", "showRowError", "rowBackgroundColorFn", "minRow", "minRowTooltip", "maxRow", "maxRowTooltip"], outputs: ["add", "remove"] }, { kind: "directive", type: i0.forwardRef(() => i2$1.ArrayFormTableHeaderDirective), selector: "[aclArrayFormTableHeader]" }, { kind: "directive", type: i0.forwardRef(() => i2$1.ArrayFormTableRowDirective), selector: "[aclArrayFormTableRow]" }, { kind: "component", type: i0.forwardRef(() => OperandFieldComponent), selector: "acl-operand-field", inputs: ["field", "readonly", "fields", "formDataState", "formErrors"], outputs: ["valueChange", "itemRemove"] }, { kind: "pipe", type: i0.forwardRef(() => PurePipe), name: "pure" }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1089
|
+
}
|
|
1090
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CrdFormArrayTableComponent, decorators: [{
|
|
1091
|
+
type: Component,
|
|
1092
|
+
args: [{ selector: 'acl-crd-array-table', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1093
|
+
FormModule,
|
|
1094
|
+
ARRAY_FORM_TABLE_MODULE,
|
|
1095
|
+
forwardRef(() => OperandFieldComponent),
|
|
1096
|
+
PurePipe,
|
|
1097
|
+
TranslatePipe,
|
|
1098
|
+
], template: "<aui-form-item [emptyAddon]=\"false\">\n <label auiFormItemLabel>{{\n arrayFieldGroup | pure: getFieldDisplayName | translate\n }}</label>\n\n <acl-array-form-table\n style=\"flex: 1\"\n (remove)=\"removeItem($event)\"\n (add)=\"addItem()\"\n [rows]=\"arrayFieldGroup.fieldLists\"\n [readonly]=\"readonly\"\n class=\"form-table__flex-layout\"\n [resourceNameTranslated]=\"\n arrayFieldGroup | pure: getFieldDisplayName | translate\n \"\n >\n <ng-container *aclArrayFormTableHeader>\n @for (field of basicArrayFieldTemplate; track field) {\n @if (field | pure: fieldIsRequired) {\n <th required>\n {{ field | pure: getFieldDisplayName | translate }}\n </th>\n } @else {\n <th>{{ field | pure: getFieldDisplayName | translate }}</th>\n }\n }\n </ng-container>\n\n <!-- Input row -->\n <ng-container *aclArrayFormTableRow=\"let fieldList; let index = index\">\n @for (field of fieldList; track trackByFn(index); let index = $index) {\n <td>\n <acl-operand-field\n [field]=\"field\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n </td>\n }\n </ng-container>\n </acl-array-form-table>\n</aui-form-item>\n", styles: [":host{flex:1}:host ::ng-deep acl-array-form-table .acl-array-form-table__readonly-mode tbody tr{height:auto}:host ::ng-deep acl-array-form-table .aui-form-item{margin-bottom:0}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__label-wrapper{display:none}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__content{min-height:auto}:host ::ng-deep acl-array-form-table .aui-form-item .aui-form-item__content .aui-form-item__control{max-width:100%}:host ::ng-deep acl-array-form-table .acl-array-form-table tbody td{vertical-align:top}\n"] }]
|
|
1099
|
+
}] });
|
|
1100
|
+
|
|
1101
|
+
class BasicAuthCreateSecretDialogComponent {
|
|
1102
|
+
constructor() {
|
|
1103
|
+
this.dialogData = inject(DIALOG_DATA);
|
|
1104
|
+
this.dialogRef = inject(DialogRef);
|
|
1105
|
+
this.fb = inject(FormBuilder);
|
|
1106
|
+
this.k8sApi = inject(K8sApiService);
|
|
1107
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
1108
|
+
this.message = inject(MessageService);
|
|
1109
|
+
this.translate = inject(TranslateService);
|
|
1110
|
+
this.submitting = false;
|
|
1111
|
+
this.K8S_RESOURCE_NAME_BASE = K8S_RESOURCE_NAME_BASE;
|
|
1112
|
+
this.usernameKey = this.dialogData.config?.usernameKey ?? 'username';
|
|
1113
|
+
this.passwordKey = this.dialogData.config?.passwordKey ?? 'password';
|
|
1114
|
+
this.usernameReadonly = !!this.dialogData.config?.usernameReadonly;
|
|
1115
|
+
this.strongPassword = !!this.dialogData.config?.strongPassword;
|
|
1116
|
+
this.password = !!this.dialogData.config?.password;
|
|
1117
|
+
this.showPassword = false;
|
|
1118
|
+
this.formGroup = this.fb.group({
|
|
1119
|
+
name: this.fb.control(this.dialogData.config?.secretName || '', [
|
|
1120
|
+
Validators.required,
|
|
1121
|
+
Validators.pattern(K8S_RESOURCE_NAME_BASE.pattern),
|
|
1122
|
+
Validators.maxLength(63),
|
|
1123
|
+
]),
|
|
1124
|
+
username: this.fb.control(this.dialogData.config?.usernameDefault || ''),
|
|
1125
|
+
password: this.fb.control('', [Validators.required]),
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
confirm() {
|
|
1129
|
+
this.form.onSubmit(null);
|
|
1130
|
+
if (!this.form.valid) {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
this.submitting = true;
|
|
1134
|
+
const value = this.form.value;
|
|
1135
|
+
const data = {
|
|
1136
|
+
[this.passwordKey]: encode(value.password),
|
|
1137
|
+
};
|
|
1138
|
+
if (value.username) {
|
|
1139
|
+
data[this.usernameKey] = encode(value.username);
|
|
1140
|
+
}
|
|
1141
|
+
const resource = {
|
|
1142
|
+
kind: 'Secret',
|
|
1143
|
+
apiVersion: 'v1',
|
|
1144
|
+
metadata: {
|
|
1145
|
+
name: value.name,
|
|
1146
|
+
namespace: this.dialogData.namespace,
|
|
1147
|
+
},
|
|
1148
|
+
type: SecretType.Opaque,
|
|
1149
|
+
data,
|
|
1150
|
+
};
|
|
1151
|
+
this.k8sApi
|
|
1152
|
+
.postResource({
|
|
1153
|
+
definition: COMMON_RESOURCE_DEFINITIONS.SECRET,
|
|
1154
|
+
cluster: this.dialogData.cluster,
|
|
1155
|
+
namespace: this.dialogData.namespace,
|
|
1156
|
+
resource,
|
|
1157
|
+
})
|
|
1158
|
+
.pipe(finalize(() => {
|
|
1159
|
+
this.submitting = false;
|
|
1160
|
+
this.cdr.markForCheck();
|
|
1161
|
+
}))
|
|
1162
|
+
.subscribe(res => {
|
|
1163
|
+
this.dialogRef.close(res);
|
|
1164
|
+
this.message.success(this.translate.get('create_success'));
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
toggleShowPassword() {
|
|
1168
|
+
this.showPassword = !this.showPassword;
|
|
1169
|
+
}
|
|
1170
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BasicAuthCreateSecretDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1171
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: BasicAuthCreateSecretDialogComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "form", first: true, predicate: FormGroupDirective, descendants: true, static: true }], ngImport: i0, template: "<aui-dialog-header>\n {{ 'create_secret' | translate }}\n</aui-dialog-header>\n\n<aui-dialog-content>\n <form\n auiForm\n [formGroup]=\"formGroup\"\n >\n <aui-form-item>\n <label auiFormItemLabel>\n {{ 'name' | translate }}\n </label>\n <input\n type=\"text\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"name\"\n />\n <acl-errors-mapper\n auiFormItemError\n [errors]=\"formGroup.get('name').errors\"\n [errorsMapper]=\"{\n pattern: K8S_RESOURCE_NAME_BASE.tip | translate,\n }\"\n ></acl-errors-mapper>\n </aui-form-item>\n <aui-form-item>\n <label auiFormItemLabel>{{ 'config' | translate }}</label>\n <div class=\"form-table__flex-layout tw-flex-1\">\n <table class=\"acl-array-form-table\">\n <thead>\n <tr>\n <th>{{ 'key' | translate }}</th>\n <th>{{ 'value' | translate }}</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <input\n type=\"text\"\n required\n aui-input\n readonly\n [ngModel]=\"usernameKey\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </td>\n <td>\n <input\n type=\"text\"\n aui-input\n auiFormItemControl\n formControlName=\"username\"\n [readonly]=\"usernameReadonly\"\n />\n </td>\n </tr>\n <tr>\n <td>\n <input\n type=\"text\"\n required\n aui-input\n readonly\n [ngModel]=\"passwordKey\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </td>\n <td>\n @if (strongPassword && !password) {\n <input\n type=\"password\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"password\"\n aclStrongPassword\n />\n }\n @if (!strongPassword && !password) {\n <input\n type=\"text\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"password\"\n />\n }\n @if (password && !strongPassword) {\n <aui-input-group\n auiFormItemControl\n required\n >\n <input\n auiFormItemControl\n aui-input\n required\n formControlName=\"password\"\n [type]=\"showPassword ? 'text' : 'password'\"\n />\n <div\n auiInputSuffix\n class=\"password__suffix\"\n (click)=\"toggleShowPassword()\"\n >\n <aui-icon\n [icon]=\"showPassword ? 'eye_s' : 'eye_slash_s'\"\n ></aui-icon>\n </div>\n </aui-input-group>\n }\n\n @if (formGroup.get('password').dirty || form?.submitted) {\n <acl-errors-mapper\n auiFormItemError\n [errors]=\"formGroup.get('password').errors\"\n ></acl-errors-mapper>\n }\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </aui-form-item>\n </form>\n</aui-dialog-content>\n\n<aui-dialog-footer>\n <button\n aui-button=\"primary\"\n (click)=\"confirm()\"\n [loading]=\"submitting\"\n [disabled]=\"submitting\"\n >\n {{ 'create' | translate }}\n </button>\n <button\n aui-button\n auiDialogClose\n >\n {{ 'cancel' | translate }}\n </button>\n</aui-dialog-footer>\n", styles: [":host::ng-deep .aui-form-item__label-wrapper{width:114px}:host::ng-deep label{word-break:break-all}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormModule }, { kind: "directive", type: i2.FormDirective, selector: "form[auiForm]", inputs: ["auiFormLabelWidth", "auiFormLabelPosition", "auiFormEmptyAddon", "auiFormInline"], exportAs: ["auiForm"] }, { kind: "component", type: i2.FormItemComponent, selector: "aui-form-item", inputs: ["labelWidth", "width", "labelPosition", "emptyAddon", "plain"] }, { kind: "directive", type: i2.FormItemErrorDirective, selector: "[auiFormItemError]" }, { kind: "directive", type: i2.FormItemLabelDirective, selector: "label[auiFormItemLabel]" }, { kind: "directive", type: i2.FormItemControlDirective, selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2.InputComponent, selector: "input[aui-input],textarea[aui-input]", inputs: ["size", "disabled"] }, { kind: "component", type: i2.InputGroupComponent, selector: "aui-input-group" }, { kind: "directive", type: i2.InputSuffixDirective, selector: "[auiInputSuffix]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[aui-button]", inputs: ["aui-button", "size", "plain", "loading", "round", "square"] }, { kind: "ngmodule", type: IconModule }, { kind: "component", type: i2.IconComponent, selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.DialogHeaderComponent, selector: "aui-dialog-header", inputs: ["divider", "closeable", "result"] }, { kind: "component", type: i2.DialogContentComponent, selector: "aui-dialog-content" }, { kind: "component", type: i2.DialogFooterComponent, selector: "aui-dialog-footer" }, { kind: "directive", type: i2.DialogCloseDirective, selector: "[auiDialogClose]", inputs: ["auiDialogClose"], exportAs: ["auiDialogClose"] }, { kind: "component", type: ErrorsMapperComponent, selector: "acl-errors-mapper", inputs: ["errors", "errorsMapper", "errorsMapperFn", "controlName", "ignoreUnknownError"] }, { kind: "directive", type: StrongPasswordDirective, selector: "input[aclStrongPassword][ngModel],input[aclStrongPassword][formControl],input[aclStrongPassword][formControlName]", inputs: ["aclStrongPassword", "required", "specialChars", "minlength", "maxlength", "notStartsWith"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1172
|
+
}
|
|
1173
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BasicAuthCreateSecretDialogComponent, decorators: [{
|
|
1174
|
+
type: Component,
|
|
1175
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1176
|
+
FormsModule,
|
|
1177
|
+
ReactiveFormsModule,
|
|
1178
|
+
FormModule,
|
|
1179
|
+
InputModule,
|
|
1180
|
+
ButtonModule,
|
|
1181
|
+
IconModule,
|
|
1182
|
+
DialogModule,
|
|
1183
|
+
ErrorsMapperComponent,
|
|
1184
|
+
StrongPasswordDirective,
|
|
1185
|
+
TranslatePipe,
|
|
1186
|
+
], template: "<aui-dialog-header>\n {{ 'create_secret' | translate }}\n</aui-dialog-header>\n\n<aui-dialog-content>\n <form\n auiForm\n [formGroup]=\"formGroup\"\n >\n <aui-form-item>\n <label auiFormItemLabel>\n {{ 'name' | translate }}\n </label>\n <input\n type=\"text\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"name\"\n />\n <acl-errors-mapper\n auiFormItemError\n [errors]=\"formGroup.get('name').errors\"\n [errorsMapper]=\"{\n pattern: K8S_RESOURCE_NAME_BASE.tip | translate,\n }\"\n ></acl-errors-mapper>\n </aui-form-item>\n <aui-form-item>\n <label auiFormItemLabel>{{ 'config' | translate }}</label>\n <div class=\"form-table__flex-layout tw-flex-1\">\n <table class=\"acl-array-form-table\">\n <thead>\n <tr>\n <th>{{ 'key' | translate }}</th>\n <th>{{ 'value' | translate }}</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <input\n type=\"text\"\n required\n aui-input\n readonly\n [ngModel]=\"usernameKey\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </td>\n <td>\n <input\n type=\"text\"\n aui-input\n auiFormItemControl\n formControlName=\"username\"\n [readonly]=\"usernameReadonly\"\n />\n </td>\n </tr>\n <tr>\n <td>\n <input\n type=\"text\"\n required\n aui-input\n readonly\n [ngModel]=\"passwordKey\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </td>\n <td>\n @if (strongPassword && !password) {\n <input\n type=\"password\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"password\"\n aclStrongPassword\n />\n }\n @if (!strongPassword && !password) {\n <input\n type=\"text\"\n required\n aui-input\n auiFormItemControl\n formControlName=\"password\"\n />\n }\n @if (password && !strongPassword) {\n <aui-input-group\n auiFormItemControl\n required\n >\n <input\n auiFormItemControl\n aui-input\n required\n formControlName=\"password\"\n [type]=\"showPassword ? 'text' : 'password'\"\n />\n <div\n auiInputSuffix\n class=\"password__suffix\"\n (click)=\"toggleShowPassword()\"\n >\n <aui-icon\n [icon]=\"showPassword ? 'eye_s' : 'eye_slash_s'\"\n ></aui-icon>\n </div>\n </aui-input-group>\n }\n\n @if (formGroup.get('password').dirty || form?.submitted) {\n <acl-errors-mapper\n auiFormItemError\n [errors]=\"formGroup.get('password').errors\"\n ></acl-errors-mapper>\n }\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </aui-form-item>\n </form>\n</aui-dialog-content>\n\n<aui-dialog-footer>\n <button\n aui-button=\"primary\"\n (click)=\"confirm()\"\n [loading]=\"submitting\"\n [disabled]=\"submitting\"\n >\n {{ 'create' | translate }}\n </button>\n <button\n aui-button\n auiDialogClose\n >\n {{ 'cancel' | translate }}\n </button>\n</aui-dialog-footer>\n", styles: [":host::ng-deep .aui-form-item__label-wrapper{width:114px}:host::ng-deep label{word-break:break-all}\n"] }]
|
|
1187
|
+
}], propDecorators: { form: [{
|
|
1188
|
+
type: ViewChild,
|
|
1189
|
+
args: [FormGroupDirective, { static: true }]
|
|
1190
|
+
}] } });
|
|
1191
|
+
|
|
1192
|
+
class BasicAuthSecretComponent extends BaseResourceFormComponent {
|
|
1193
|
+
constructor() {
|
|
1194
|
+
super(inject(Injector));
|
|
1195
|
+
this.k8sApi = inject(K8sApiService);
|
|
1196
|
+
this.crdForm = inject(CrdFormComponent);
|
|
1197
|
+
this.dialog = inject(DialogService);
|
|
1198
|
+
this.k8sUtil = inject(K8sUtilService);
|
|
1199
|
+
this.config$ = this.field$.pipe(filter(field => !!field), map$1(field => getBasicAuthSecretConfig(field)), publishRef());
|
|
1200
|
+
this.reload$ = new Subject();
|
|
1201
|
+
this.resources$ = combineLatest([
|
|
1202
|
+
this.crdForm.formContext$,
|
|
1203
|
+
this.config$,
|
|
1204
|
+
this.reload$.pipe(startWith(null)),
|
|
1205
|
+
]).pipe(switchMap(([{ cluster, namespace }, config]) => this.k8sApi
|
|
1206
|
+
.getResourceList({
|
|
1207
|
+
definition: COMMON_RESOURCE_DEFINITIONS.SECRET,
|
|
1208
|
+
namespace: config?.namespace ?? namespace,
|
|
1209
|
+
cluster: config?.cluster ?? cluster,
|
|
1210
|
+
})
|
|
1211
|
+
.pipe(map$1(res => res.items), skipError([]), map$1(items => items.filter(item => [SecretType.Opaque, SecretType.BasicAuth].includes(item.type))))), publishRef());
|
|
1212
|
+
this.resources$.pipe(takeUntil(this.destroy$)).subscribe(items => {
|
|
1213
|
+
if (this.form.value &&
|
|
1214
|
+
!items.some(item => this.k8sUtil.getName(item) === this.form.value)) {
|
|
1215
|
+
this.form.reset();
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
createForm() {
|
|
1220
|
+
// 使用 control 的 validators 而非 required 指令目地是后者会导致 control 实例化后 dirty
|
|
1221
|
+
return this.fb.control(null, this.required ? Validators.required : null);
|
|
1222
|
+
}
|
|
1223
|
+
create() {
|
|
1224
|
+
this.config$.subscribe(config => {
|
|
1225
|
+
const dialogRef = this.dialog.open(BasicAuthCreateSecretDialogComponent, {
|
|
1226
|
+
size: DialogSize.Big,
|
|
1227
|
+
data: {
|
|
1228
|
+
cluster: config?.cluster ?? this.crdForm.formContext.cluster,
|
|
1229
|
+
namespace: config?.namespace ?? this.crdForm.formContext.namespace,
|
|
1230
|
+
config,
|
|
1231
|
+
},
|
|
1232
|
+
});
|
|
1233
|
+
return dialogRef.afterClosed().subscribe((res) => {
|
|
1234
|
+
if (res) {
|
|
1235
|
+
this.reload$.next();
|
|
1236
|
+
this.form.setValue(res.metadata.name);
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BasicAuthSecretComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1242
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: BasicAuthSecretComponent, isStandalone: true, selector: "acl-crd-basic-auth-secret", inputs: { required: "required", field: "field" }, usesInheritance: true, ngImport: i0, template: "<aui-select\n class=\"tw-flex-1 tw-mr-[4px]\"\n [formControl]=\"form\"\n>\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n <aui-option-placeholder>{{ 'no_data' | translate }}</aui-option-placeholder>\n</aui-select>\n<button\n aui-button=\"default\"\n type=\"button\"\n (click)=\"create()\"\n>\n {{ 'create' | translate }}\n</button>\n", styles: [":host{display:flex}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.SelectComponent, selector: "aui-select" }, { kind: "component", type: i2.OptionComponent, selector: "aui-option", inputs: ["label", "labelContext", "value", "disabled"] }, { kind: "component", type: i2.OptionPlaceholderComponent, selector: "aui-option-placeholder" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[aui-button]", inputs: ["aui-button", "size", "plain", "loading", "round", "square"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: i4.K8sNamePipe, name: "aclName" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1243
|
+
}
|
|
1244
|
+
__decorate([
|
|
1245
|
+
ObservableInput(),
|
|
1246
|
+
__metadata("design:type", Observable)
|
|
1247
|
+
], BasicAuthSecretComponent.prototype, "field$", void 0);
|
|
1248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BasicAuthSecretComponent, decorators: [{
|
|
1249
|
+
type: Component,
|
|
1250
|
+
args: [{ selector: 'acl-crd-basic-auth-secret', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1251
|
+
CommonModule,
|
|
1252
|
+
ReactiveFormsModule,
|
|
1253
|
+
SelectModule,
|
|
1254
|
+
ButtonModule,
|
|
1255
|
+
TranslatePipe,
|
|
1256
|
+
K8S_UTIL_PIPES_MODULE,
|
|
1257
|
+
], template: "<aui-select\n class=\"tw-flex-1 tw-mr-[4px]\"\n [formControl]=\"form\"\n>\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n <aui-option-placeholder>{{ 'no_data' | translate }}</aui-option-placeholder>\n</aui-select>\n<button\n aui-button=\"default\"\n type=\"button\"\n (click)=\"create()\"\n>\n {{ 'create' | translate }}\n</button>\n", styles: [":host{display:flex}\n"] }]
|
|
1258
|
+
}], ctorParameters: () => [], propDecorators: { required: [{
|
|
1259
|
+
type: Input
|
|
1260
|
+
}], field: [{
|
|
1261
|
+
type: Input
|
|
1262
|
+
}], field$: [] } });
|
|
1263
|
+
|
|
1264
|
+
function findResourceType(type, definitions) {
|
|
1265
|
+
type = plural(type.toLowerCase());
|
|
1266
|
+
return Object.entries(definitions).find(([_k, v]) => v.type === type)?.[0];
|
|
1267
|
+
}
|
|
1268
|
+
function plural(word) {
|
|
1269
|
+
const plural = {
|
|
1270
|
+
'(quiz)$': '$1zes',
|
|
1271
|
+
'^(ox)$': '$1en',
|
|
1272
|
+
'([m|l])ouse$': '$1ice',
|
|
1273
|
+
// cspell:disable-next-line
|
|
1274
|
+
'(matr|vert|ind)ix|ex$': '$1ices',
|
|
1275
|
+
'(x|ch|ss|sh)$': '$1es',
|
|
1276
|
+
// cspell:disable-next-line
|
|
1277
|
+
'([^aeiouy]|qu)y$': '$1ies',
|
|
1278
|
+
'(hive)$': '$1s',
|
|
1279
|
+
'(?:([^f])fe|([lr])f)$': '$1$2ves',
|
|
1280
|
+
// cspell:disable-next-line
|
|
1281
|
+
'(shea|lea|loa|thie)f$': '$1ves',
|
|
1282
|
+
sis$: 'ses',
|
|
1283
|
+
'([ti])um$': '$1a',
|
|
1284
|
+
// cspell:disable-next-line
|
|
1285
|
+
'(tomat|potat|ech|her|vet)o$': '$1oes',
|
|
1286
|
+
'(bu)s$': '$1ses',
|
|
1287
|
+
'(alias)$': '$1es',
|
|
1288
|
+
// cspell:disable-next-line
|
|
1289
|
+
'(octop)us$': '$1i',
|
|
1290
|
+
'(ax|test)is$': '$1es',
|
|
1291
|
+
'(us)$': '$1es',
|
|
1292
|
+
'([^s]+)$': '$1s',
|
|
1293
|
+
};
|
|
1294
|
+
// check for matches using regular expressions
|
|
1295
|
+
Object.keys(plural).forEach(reg => {
|
|
1296
|
+
const pattern = new RegExp(reg, 'i');
|
|
1297
|
+
if (pattern.test(word)) {
|
|
1298
|
+
word = word.replace(pattern, plural[reg]);
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
return word;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
class K8sResourcePrefixComponent extends BaseResourceFormComponent {
|
|
1305
|
+
constructor() {
|
|
1306
|
+
super(inject(Injector));
|
|
1307
|
+
this.k8sApi = inject(K8sApiService);
|
|
1308
|
+
this.crdForm = inject(CrdFormComponent);
|
|
1309
|
+
this.resourceDefinitions = inject(TOKEN_RESOURCE_DEFINITIONS);
|
|
1310
|
+
this.init$$ = new BehaviorSubject(false);
|
|
1311
|
+
this.readonly = false;
|
|
1312
|
+
this.options$ = this.field$.pipe(map$1(field => getK8sResourcePrefixOptions(field)));
|
|
1313
|
+
this.resources$ = this.options$.pipe(switchMap(option => this.k8sApi[option.isGlobal ? 'getGlobalResourceList' : 'getResourceList']({
|
|
1314
|
+
type: findResourceType(option.type, this.resourceDefinitions),
|
|
1315
|
+
...(option.namespaced
|
|
1316
|
+
? option.isGlobal
|
|
1317
|
+
? { namespaced: true }
|
|
1318
|
+
: { namespace: this.crdForm?.formContext?.namespace }
|
|
1319
|
+
: null),
|
|
1320
|
+
...(!option.isGlobal && {
|
|
1321
|
+
cluster: this.crdForm?.formContext?.cluster,
|
|
1322
|
+
}),
|
|
1323
|
+
...(option.labelSelector && {
|
|
1324
|
+
queryParams: { labelSelector: option.labelSelector },
|
|
1325
|
+
}),
|
|
1326
|
+
}).pipe(pluck('items'))), skipError(), tap(() => this.init$$.next(true)));
|
|
1327
|
+
}
|
|
1328
|
+
createForm() {
|
|
1329
|
+
return this.fb.control(null);
|
|
1330
|
+
}
|
|
1331
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: K8sResourcePrefixComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1332
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: K8sResourcePrefixComponent, isStandalone: true, selector: "acl-crd-resource-prefix", inputs: { field: "field", readonly: "readonly", formErrors: "formErrors" }, usesInheritance: true, ngImport: i0, template: "@if ((options$ | async)?.multiple) {\n <aui-multi-select\n class=\"select\"\n [formControl]=\"form\"\n [hidden]=\"readonly || !(init$$ | async)\"\n [maxRowCount]=\"3\"\n >\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n </aui-multi-select>\n @if (readonly) {\n @for (tag of $any(form.value); track tag; let ind = $index) {\n <aui-tag\n type=\"info\"\n size=\"mini\"\n [border]=\"true\"\n [auiTooltip]=\"tag\"\n >\n {{ tag }}\n </aui-tag>\n }\n }\n} @else {\n <aui-select\n [formControl]=\"form\"\n class=\"select\"\n [hidden]=\"readonly\"\n >\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n <aui-option-placeholder>{{ 'no_data' | translate }}</aui-option-placeholder>\n </aui-select>\n @if (readonly) {\n <span>{{ form.value }}</span>\n }\n}\n", styles: [":host{flex:1}.select{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.SelectComponent, selector: "aui-select" }, { kind: "component", type: i2.OptionComponent, selector: "aui-option", inputs: ["label", "labelContext", "value", "disabled"] }, { kind: "component", type: i2.OptionPlaceholderComponent, selector: "aui-option-placeholder" }, { kind: "component", type: i2.MultiSelectComponent, selector: "aui-multi-select", inputs: ["tagClassFn", "maxRowCount", "customRowHeight", "allowSelectAll"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i2.TagComponent, selector: "aui-tag", inputs: ["type", "size", "closeable", "border", "solid", "invalid", "round", "color", "allowClick"], outputs: ["close"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2.TooltipDirective, selector: "[auiTooltip]", inputs: ["auiTooltip", "auiTooltipContext", "auiTooltipClass", "auiTooltipType", "auiTooltipPosition", "auiTooltipTrigger", "auiTooltipDisabled", "auiTooltipHideOnClick", "auiTooltipAnimType"], outputs: ["auiTooltipVisibleChange"], exportAs: ["auiTooltip"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: i4.K8sNamePipe, name: "aclName" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1333
|
+
}
|
|
1334
|
+
__decorate([
|
|
1335
|
+
ObservableInput(),
|
|
1336
|
+
__metadata("design:type", Observable)
|
|
1337
|
+
], K8sResourcePrefixComponent.prototype, "field$", void 0);
|
|
1338
|
+
__decorate([
|
|
1339
|
+
ValueHook(function (formErrors) {
|
|
1340
|
+
this.form?.setErrors(isEmpty(formErrors) ? null : formErrors);
|
|
1341
|
+
}),
|
|
1342
|
+
__metadata("design:type", Object)
|
|
1343
|
+
], K8sResourcePrefixComponent.prototype, "formErrors", void 0);
|
|
1344
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: K8sResourcePrefixComponent, decorators: [{
|
|
1345
|
+
type: Component,
|
|
1346
|
+
args: [{ selector: 'acl-crd-resource-prefix', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1347
|
+
CommonModule,
|
|
1348
|
+
ReactiveFormsModule,
|
|
1349
|
+
SelectModule,
|
|
1350
|
+
TagModule,
|
|
1351
|
+
TooltipModule,
|
|
1352
|
+
TranslatePipe,
|
|
1353
|
+
K8S_UTIL_PIPES_MODULE,
|
|
1354
|
+
], template: "@if ((options$ | async)?.multiple) {\n <aui-multi-select\n class=\"select\"\n [formControl]=\"form\"\n [hidden]=\"readonly || !(init$$ | async)\"\n [maxRowCount]=\"3\"\n >\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n </aui-multi-select>\n @if (readonly) {\n @for (tag of $any(form.value); track tag; let ind = $index) {\n <aui-tag\n type=\"info\"\n size=\"mini\"\n [border]=\"true\"\n [auiTooltip]=\"tag\"\n >\n {{ tag }}\n </aui-tag>\n }\n }\n} @else {\n <aui-select\n [formControl]=\"form\"\n class=\"select\"\n [hidden]=\"readonly\"\n >\n @for (item of resources$ | async; track item) {\n <aui-option\n [value]=\"item | aclName\"\n [label]=\"item | aclName\"\n >\n {{ item | aclName }}\n </aui-option>\n }\n <aui-option-placeholder>{{ 'no_data' | translate }}</aui-option-placeholder>\n </aui-select>\n @if (readonly) {\n <span>{{ form.value }}</span>\n }\n}\n", styles: [":host{flex:1}.select{display:block}\n"] }]
|
|
1355
|
+
}], ctorParameters: () => [], propDecorators: { field: [{
|
|
1356
|
+
type: Input
|
|
1357
|
+
}], field$: [], readonly: [{
|
|
1358
|
+
type: Input
|
|
1359
|
+
}], formErrors: [{
|
|
1360
|
+
type: Input
|
|
1361
|
+
}] } });
|
|
1362
|
+
|
|
1363
|
+
function isLink(field) {
|
|
1364
|
+
const { capabilities } = field;
|
|
1365
|
+
return capabilities.some(c => c.startsWith(`${SpecCapability.text}:link`));
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* 获取链接参数
|
|
1369
|
+
*
|
|
1370
|
+
* @see https://jira.alauda.cn/browse/ACP-32703
|
|
1371
|
+
*/
|
|
1372
|
+
function resolveLinkOptions(field) {
|
|
1373
|
+
const linkCapability = field.capabilities.find(capability => capability.startsWith(SpecCapability.text));
|
|
1374
|
+
const target = linkCapability?.split(`${SpecCapability.text}:link:`)?.[1];
|
|
1375
|
+
return { target: target || '_blank' };
|
|
1376
|
+
}
|
|
1377
|
+
class LinkComponent extends createNestedFormControl({
|
|
1378
|
+
autoEmitChange: true,
|
|
1379
|
+
}) {
|
|
1380
|
+
get options() {
|
|
1381
|
+
return resolveLinkOptions(this.field);
|
|
1382
|
+
}
|
|
1383
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LinkComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1384
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: LinkComponent, isStandalone: true, selector: "acl-crd-link", inputs: { field: "field" }, providers: [
|
|
1385
|
+
{
|
|
1386
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1387
|
+
useExisting: forwardRef(() => LinkComponent),
|
|
1388
|
+
multi: true,
|
|
1389
|
+
},
|
|
1390
|
+
], usesInheritance: true, ngImport: i0, template: "<a\n [href]=\"model | aclSanitize: 'url'\"\n [target]=\"options.target\"\n>\n <span>{{ model }}</span>\n @if (options.target === '_blank') {\n <aui-icon\n icon=\"jump\"\n margin=\"left\"\n >\n </aui-icon>\n }\n</a>\n", styles: [":host{flex:none!important}\n"], dependencies: [{ kind: "ngmodule", type: IconModule }, { kind: "component", type: i2.IconComponent, selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "pipe", type: SanitizePipe, name: "aclSanitize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1391
|
+
}
|
|
1392
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LinkComponent, decorators: [{
|
|
1393
|
+
type: Component,
|
|
1394
|
+
args: [{ standalone: true, selector: 'acl-crd-link', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
1395
|
+
{
|
|
1396
|
+
provide: NG_VALUE_ACCESSOR,
|
|
1397
|
+
useExisting: forwardRef(() => LinkComponent),
|
|
1398
|
+
multi: true,
|
|
1399
|
+
},
|
|
1400
|
+
], imports: [IconModule, SanitizePipe], template: "<a\n [href]=\"model | aclSanitize: 'url'\"\n [target]=\"options.target\"\n>\n <span>{{ model }}</span>\n @if (options.target === '_blank') {\n <aui-icon\n icon=\"jump\"\n margin=\"left\"\n >\n </aui-icon>\n }\n</a>\n", styles: [":host{flex:none!important}\n"] }]
|
|
1401
|
+
}], propDecorators: { field: [{
|
|
1402
|
+
type: Input
|
|
1403
|
+
}] } });
|
|
1404
|
+
|
|
1405
|
+
function getConfigurations(specCapability, part) {
|
|
1406
|
+
const regExp = new RegExp(`^${escapeRegExp(part)}(.*)$`);
|
|
1407
|
+
const [_, config] = regExp.exec(specCapability) || [];
|
|
1408
|
+
return config;
|
|
1409
|
+
}
|
|
1410
|
+
// urn:alm:descriptor:com.tectonic.ui:resourceRequirements:required
|
|
1411
|
+
// urn:alm:descriptor:com.tectonic.ui:resourceRequirements:required:limit-memory&limit-cpu&request-memory&request-cpu
|
|
1412
|
+
// urn:alm:descriptor:com.tectonic.ui:resourceRequirements:required:limit&request-memory
|
|
1413
|
+
function resolveRequirementsRequiredOptions(field) {
|
|
1414
|
+
if (!field) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const options = field.capabilities.filter(descriptor => descriptor.startsWith(SpecCapability.resourceRequirements));
|
|
1418
|
+
const allRequired = options.includes(`${SpecCapability.resourceRequirements}:required`);
|
|
1419
|
+
if (allRequired) {
|
|
1420
|
+
return {
|
|
1421
|
+
required: {
|
|
1422
|
+
limit: ['cpu', 'memory'],
|
|
1423
|
+
request: ['cpu', 'memory'],
|
|
1424
|
+
},
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
const requiredMap = options.reduce((prev, curr) => {
|
|
1428
|
+
const partsStr = curr.split(`${SpecCapability.resourceRequirements}:required:`)?.[1];
|
|
1429
|
+
partsStr?.split('&')?.forEach(optionStr => {
|
|
1430
|
+
const options = optionStr?.split('-');
|
|
1431
|
+
if (options.length === 1) {
|
|
1432
|
+
prev[options[0]].add('memory').add('cpu');
|
|
1433
|
+
}
|
|
1434
|
+
if (options.length > 1) {
|
|
1435
|
+
prev[options[0]].add(options[1]);
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
return prev;
|
|
1439
|
+
}, {
|
|
1440
|
+
limit: new Set(),
|
|
1441
|
+
request: new Set(),
|
|
1442
|
+
});
|
|
1443
|
+
return {
|
|
1444
|
+
required: {
|
|
1445
|
+
limit: Array.from(requiredMap.limit),
|
|
1446
|
+
request: Array.from(requiredMap.request),
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
// urn:alm:descriptor:com.tectonic.ui:resourceRequirements:min:{"limits": {"cpu":"2c", "memory":"500Mi"}, "requests":{"cpu":"100m", "memory":"250Mi"}}
|
|
1451
|
+
// urn:alm:descriptor:com.tectonic.ui:resourceRequirements:max:{"limits": {"cpu":"4c", "memory":"500Mi"}, "requests":{"cpu":"4c", "memory":"500Mi"}}
|
|
1452
|
+
function resolveRequirementsMinOrMaxOptions(field) {
|
|
1453
|
+
if (!field) {
|
|
1454
|
+
return null;
|
|
1455
|
+
}
|
|
1456
|
+
const options = field.capabilities.filter(descriptor => descriptor.startsWith(SpecCapability.resourceRequirements));
|
|
1457
|
+
const getConfigurationMap = (option, capability) => {
|
|
1458
|
+
const configuration = getConfigurations(option, capability);
|
|
1459
|
+
const { limits, requests } = parseJson(configuration, {
|
|
1460
|
+
limits: { cpu: null, memory: null },
|
|
1461
|
+
requests: { cpu: null, memory: null },
|
|
1462
|
+
});
|
|
1463
|
+
return { limits, requests };
|
|
1464
|
+
};
|
|
1465
|
+
const getResourceMinOrMaxMap = (suffix) => {
|
|
1466
|
+
const option = options.find(option => option.includes(`${SpecCapability.resourceRequirements}:${suffix}:`));
|
|
1467
|
+
return getConfigurationMap(option, `${SpecCapability.resourceRequirements}:${suffix}:`);
|
|
1468
|
+
};
|
|
1469
|
+
return {
|
|
1470
|
+
min: getResourceMinOrMaxMap('min'),
|
|
1471
|
+
max: getResourceMinOrMaxMap('max'),
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function handleValidationErrors(valueCtrl, errors) {
|
|
1476
|
+
// 这里避免覆盖其它 errors
|
|
1477
|
+
const combinedErrors = { ...valueCtrl.errors, ...errors };
|
|
1478
|
+
valueCtrl.setErrors(combinedErrors);
|
|
1479
|
+
return combinedErrors;
|
|
1480
|
+
}
|
|
1481
|
+
function valueFormat(type, value) {
|
|
1482
|
+
return type === 'cpu' ? formatCPU(value) : formatMemory(value);
|
|
1483
|
+
}
|
|
1484
|
+
function getCurrentValue(group, type) {
|
|
1485
|
+
const valueCtrl = group.get('value');
|
|
1486
|
+
const value = valueCtrl.value;
|
|
1487
|
+
const unit = group.get('unit').value;
|
|
1488
|
+
return valueFormat(type, (value || 0) + unit);
|
|
1489
|
+
}
|
|
1490
|
+
class ResourceRequirementsComponent extends BaseResourceFormGroupComponent {
|
|
1491
|
+
get requiredOptions() {
|
|
1492
|
+
return resolveRequirementsRequiredOptions(this.fieldComponent?.field);
|
|
1493
|
+
}
|
|
1494
|
+
get limitMemoryRequired() {
|
|
1495
|
+
return this.requiredOptions.required?.limit?.includes('memory');
|
|
1496
|
+
}
|
|
1497
|
+
get limitCpuRequired() {
|
|
1498
|
+
return this.requiredOptions.required?.limit?.includes('cpu');
|
|
1499
|
+
}
|
|
1500
|
+
get requestMemoryRequired() {
|
|
1501
|
+
return this.requiredOptions.required?.request?.includes('memory');
|
|
1502
|
+
}
|
|
1503
|
+
get requestCpuRequired() {
|
|
1504
|
+
return this.requiredOptions.required?.request?.includes('cpu');
|
|
1505
|
+
}
|
|
1506
|
+
get minMaxOptions() {
|
|
1507
|
+
return resolveRequirementsMinOrMaxOptions(this.fieldComponent?.field);
|
|
1508
|
+
}
|
|
1509
|
+
get limitMemoryMin() {
|
|
1510
|
+
return this.minMaxOptions.min.limits?.memory;
|
|
1511
|
+
}
|
|
1512
|
+
get limitCpuMin() {
|
|
1513
|
+
return this.minMaxOptions.min.limits?.cpu;
|
|
1514
|
+
}
|
|
1515
|
+
get requestMemoryMin() {
|
|
1516
|
+
return this.minMaxOptions.min.requests?.memory;
|
|
1517
|
+
}
|
|
1518
|
+
get requestCpuMin() {
|
|
1519
|
+
return this.minMaxOptions.min.requests?.cpu;
|
|
1520
|
+
}
|
|
1521
|
+
get limitMemoryMax() {
|
|
1522
|
+
return this.minMaxOptions.max.limits?.memory;
|
|
1523
|
+
}
|
|
1524
|
+
get limitCpuMax() {
|
|
1525
|
+
return this.minMaxOptions.max.limits?.cpu;
|
|
1526
|
+
}
|
|
1527
|
+
get requestMemoryMax() {
|
|
1528
|
+
return this.minMaxOptions.max.requests?.memory;
|
|
1529
|
+
}
|
|
1530
|
+
get requestCpuMax() {
|
|
1531
|
+
return this.minMaxOptions.max.requests?.cpu;
|
|
1532
|
+
}
|
|
1533
|
+
constructor() {
|
|
1534
|
+
super(inject(Injector));
|
|
1535
|
+
this.resourceUnits = resourceUnits;
|
|
1536
|
+
this.getResourceValue = getResourceValue;
|
|
1537
|
+
this.getResourceViewModel = getResourceViewModel;
|
|
1538
|
+
this.fieldComponent = inject(forwardRef(() => OperandFieldComponent), {
|
|
1539
|
+
optional: true,
|
|
1540
|
+
});
|
|
1541
|
+
this.controlContainer = inject(ControlContainer, {
|
|
1542
|
+
optional: true,
|
|
1543
|
+
});
|
|
1544
|
+
this.translate = inject(TranslateService);
|
|
1545
|
+
}
|
|
1546
|
+
ngOnInit() {
|
|
1547
|
+
super.ngOnInit();
|
|
1548
|
+
const greaterValidatorSubscriptions = [
|
|
1549
|
+
initGreaterValidator('cpu', this.form),
|
|
1550
|
+
initGreaterValidator('memory', this.form),
|
|
1551
|
+
];
|
|
1552
|
+
this.destroy$.subscribe(() => {
|
|
1553
|
+
greaterValidatorSubscriptions.forEach(sub => sub.unsubscribe());
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
createForm() {
|
|
1557
|
+
return this.fb.group({
|
|
1558
|
+
limits: this.fb.group({
|
|
1559
|
+
cpu: this.fb.group({
|
|
1560
|
+
value: '',
|
|
1561
|
+
unit: 'c',
|
|
1562
|
+
}, {
|
|
1563
|
+
validators: [
|
|
1564
|
+
this.minValidator(this.limitCpuMin, 'cpu'),
|
|
1565
|
+
this.maxValidator(this.limitCpuMax, 'cpu'),
|
|
1566
|
+
],
|
|
1567
|
+
}),
|
|
1568
|
+
memory: this.fb.group({
|
|
1569
|
+
value: '',
|
|
1570
|
+
unit: 'Mi',
|
|
1571
|
+
}, {
|
|
1572
|
+
validators: [
|
|
1573
|
+
this.minValidator(this.limitMemoryMin, 'memory'),
|
|
1574
|
+
this.maxValidator(this.limitMemoryMax, 'memory'),
|
|
1575
|
+
],
|
|
1576
|
+
}),
|
|
1577
|
+
}),
|
|
1578
|
+
requests: this.fb.group({
|
|
1579
|
+
cpu: this.fb.group({
|
|
1580
|
+
value: '',
|
|
1581
|
+
unit: 'c',
|
|
1582
|
+
}, {
|
|
1583
|
+
validators: [
|
|
1584
|
+
this.minValidator(this.requestCpuMin, 'cpu'),
|
|
1585
|
+
this.maxValidator(this.requestCpuMax, 'cpu'),
|
|
1586
|
+
],
|
|
1587
|
+
}),
|
|
1588
|
+
memory: this.fb.group({
|
|
1589
|
+
value: '',
|
|
1590
|
+
unit: 'Mi',
|
|
1591
|
+
}, {
|
|
1592
|
+
validators: [
|
|
1593
|
+
this.minValidator(this.requestMemoryMin, 'memory'),
|
|
1594
|
+
this.maxValidator(this.requestMemoryMax, 'memory'),
|
|
1595
|
+
],
|
|
1596
|
+
}),
|
|
1597
|
+
}),
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
getDefaultFormModel() {
|
|
1601
|
+
return {
|
|
1602
|
+
limits: {
|
|
1603
|
+
cpu: {
|
|
1604
|
+
value: '',
|
|
1605
|
+
unit: 'c',
|
|
1606
|
+
},
|
|
1607
|
+
memory: {
|
|
1608
|
+
value: '',
|
|
1609
|
+
unit: 'Mi',
|
|
1610
|
+
},
|
|
1611
|
+
},
|
|
1612
|
+
requests: {
|
|
1613
|
+
cpu: {
|
|
1614
|
+
value: '',
|
|
1615
|
+
unit: 'c',
|
|
1616
|
+
},
|
|
1617
|
+
memory: {
|
|
1618
|
+
value: '',
|
|
1619
|
+
unit: 'Mi',
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
adaptFormModel(formModel) {
|
|
1625
|
+
this.formatFormModel(formModel);
|
|
1626
|
+
return {
|
|
1627
|
+
requests: transferResource(formModel.requests),
|
|
1628
|
+
limits: transferResource(formModel.limits),
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
minValidator(min, type) {
|
|
1632
|
+
return (group) => {
|
|
1633
|
+
if (!min) {
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
const valueCtrl = group.get('value');
|
|
1637
|
+
const currentValue = getCurrentValue(group, type);
|
|
1638
|
+
const minThreshold = valueFormat(type, min);
|
|
1639
|
+
if (currentValue < minThreshold) {
|
|
1640
|
+
return handleValidationErrors(valueCtrl, { min: true });
|
|
1641
|
+
}
|
|
1642
|
+
const errors = omit(valueCtrl.errors, 'min');
|
|
1643
|
+
valueCtrl.setErrors(isEmpty(errors) ? null : errors);
|
|
1644
|
+
return null;
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
maxValidator(max, type) {
|
|
1648
|
+
return (group) => {
|
|
1649
|
+
if (!max) {
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
const valueCtrl = group.get('value');
|
|
1653
|
+
const currentValue = getCurrentValue(group, type);
|
|
1654
|
+
const maxThreshold = valueFormat(type, max);
|
|
1655
|
+
if (currentValue > maxThreshold) {
|
|
1656
|
+
return handleValidationErrors(valueCtrl, { max: true });
|
|
1657
|
+
}
|
|
1658
|
+
const errors = omit(valueCtrl.errors, 'max');
|
|
1659
|
+
valueCtrl.setErrors(isEmpty(errors) ? null : errors);
|
|
1660
|
+
return null;
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
formatFormModel(formModel) {
|
|
1664
|
+
Object.values(formModel).forEach(modelValue => {
|
|
1665
|
+
Object.values(modelValue).forEach(v => {
|
|
1666
|
+
v.value = `${v.value}`;
|
|
1667
|
+
});
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
getCpuUnitLabel(value) {
|
|
1671
|
+
return value === 'm'
|
|
1672
|
+
? ''
|
|
1673
|
+
: resourceUnits.cpu.find(unit => unit.value === value).label;
|
|
1674
|
+
}
|
|
1675
|
+
adaptResourceModel(resources) {
|
|
1676
|
+
const resourcesModel = {};
|
|
1677
|
+
if (resources) {
|
|
1678
|
+
Object.keys(resources).forEach(kind => {
|
|
1679
|
+
const resourceKind = kind;
|
|
1680
|
+
resourcesModel[resourceKind] = Object.keys(resources[resourceKind])
|
|
1681
|
+
.filter(key => RESOURCE_MAC_TYPES.includes(key))
|
|
1682
|
+
.reduce((item, type) => {
|
|
1683
|
+
const resourceType = type;
|
|
1684
|
+
item[resourceType] = getResourceViewModel(resources[resourceKind][resourceType], resourceType);
|
|
1685
|
+
return item;
|
|
1686
|
+
}, {});
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
return merge(this.getDefaultFormModel(), resourcesModel);
|
|
1690
|
+
}
|
|
1691
|
+
getMinOrMaxErrorsMapper(value, kind, type, _locale) {
|
|
1692
|
+
const resourceCtrl = getResourceViewModel(value, type);
|
|
1693
|
+
return this.translate.get(`warning_${kind}`, {
|
|
1694
|
+
value: resourceCtrl.value + this.translate.get(resourceCtrl.unit),
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ResourceRequirementsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1698
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: ResourceRequirementsComponent, isStandalone: true, selector: "acl-crd-resource-requirements", inputs: { readOnly: "readOnly", field: "field" }, usesInheritance: true, ngImport: i0, template: "@if (!readOnly) {\n <form [formGroup]=\"form\">\n <div class=\"resource_requirements\">\n <div class=\"input-group-label\">\n {{ 'resource_requirements_requests' | translate }}\n </div>\n <div\n class=\"input-group-wrapper tw-items-start\"\n formGroupName=\"requests\"\n >\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper cpu\"\n formGroupName=\"cpu\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': requestCpuRequired,\n }\"\n >\n CPU\n </div>\n <input\n aui-input\n type=\"number\"\n [required]=\"requestCpuRequired\"\n auiFormItemControl\n formControlName=\"value\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['cpu']; track unit) {\n <aui-option\n [value]=\"unit.value\"\n [label]=\"unit.label | translate\"\n >\n {{ unit.label | translate }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('requests.cpu.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('requests.cpu.value').errors\"\n [errorsMapper]=\"{\n min:\n requestCpuMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'cpu'\n : translate.locale,\n max:\n requestCpuMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'cpu'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper memory\"\n [formGroupName]=\"'memory'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': requestMemoryRequired,\n }\"\n >\n {{ 'memory' | translate }}\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"requestMemoryRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['memory']; track unit) {\n <aui-option [value]=\"unit\">\n {{ unit }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('requests.memory.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('requests.memory.value').errors\"\n [errorsMapper]=\"{\n min:\n requestMemoryMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'memory'\n : translate.locale,\n max:\n requestMemoryMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'memory'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n </div>\n </div>\n <div class=\"resource_requirements\">\n <div class=\"input-group-label\">\n {{ 'resource_requirements_limits' | translate }}\n </div>\n <div\n class=\"input-group-wrapper tw-items-start\"\n formGroupName=\"limits\"\n >\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper cpu\"\n [formGroupName]=\"'cpu'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': limitCpuRequired,\n }\"\n >\n CPU\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"limitCpuRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['cpu']; track unit) {\n <aui-option\n [value]=\"unit.value\"\n [label]=\"unit.label | translate\"\n >\n {{ unit.label | translate }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('limits.cpu.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('limits.cpu.value').errors\"\n [errorsMapper]=\"{\n cpu_max: 'request_greater_than_limits_error' | translate,\n min:\n limitCpuMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'cpu'\n : translate.locale,\n max:\n limitCpuMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'cpu'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper memory\"\n [formGroupName]=\"'memory'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': limitMemoryRequired,\n }\"\n >\n {{ 'memory' | translate }}\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"limitMemoryRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['memory']; track unit) {\n <aui-option [value]=\"unit\">\n {{ unit }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('limits.memory.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('limits.memory.value').errors\"\n [errorsMapper]=\"{\n memory_max: 'request_greater_than_limits_error' | translate,\n min:\n limitMemoryMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'memory'\n : translate.locale,\n max:\n limitMemoryMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'memory'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n </div>\n </div>\n </form>\n} @else {\n <div class=\"resource_requirements-display\">\n <div class=\"resource_requirements-display-item\">\n <label class=\"resource_requirements-display-item__label\">{{\n 'resource_requirements_requests' | translate\n }}</label>\n <aui-icon\n icon=\"prod:cpu\"\n size=\"16px\"\n color=\"#6359b4\"\n margin=\"right\"\n [title]=\"'cpu' | translate\"\n ></aui-icon\n ><span class=\"tw-mr-16\"\n >{{\n form.get('requests').get('cpu').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}{{\n form.get('requests').get('cpu.unit').value\n | pure: getCpuUnitLabel\n | translate\n }}\n </span>\n <aui-icon\n icon=\"prod:memory\"\n size=\"16px\"\n color=\"#007cb5\"\n margin=\"right\"\n [title]=\"'memory' | translate\"\n ></aui-icon\n >{{\n form.get('requests').get('memory').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}\n </div>\n <div class=\"resource_requirements-display-item\">\n <label class=\"resource_requirements-display-item__label\">{{\n 'resource_requirements_limits' | translate\n }}</label>\n <aui-icon\n icon=\"prod:cpu\"\n size=\"16px\"\n color=\"#6359b4\"\n margin=\"right\"\n [title]=\"'cpu' | translate\"\n ></aui-icon\n ><span class=\"tw-mr-16\"\n >{{\n form.get('limits').get('cpu').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}{{\n form.get('limits').get('cpu.unit').value\n | pure: getCpuUnitLabel\n | translate\n }}\n </span>\n <aui-icon\n icon=\"prod:memory\"\n size=\"16px\"\n color=\"#007cb5\"\n margin=\"right\"\n [title]=\"'memory' | translate\"\n ></aui-icon\n >{{\n form.get('limits').get('memory').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}\n </div>\n </div>\n}\n", styles: [":host .resource_requirements:not(:last-of-type){margin-bottom:8px}:host .input-group-wrapper{display:flex}:host .input-group-label{line-height:var(--aui-inline-height-m)}:host aui-input-group{flex:1}:host aui-input-group ::ng-deep .aui-input-group__addon--after{padding:0}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select{width:60px}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input-group{width:62px}html:not([lang|=zh]) :host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select{width:78px}html:not([lang|=zh]) :host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input-group{width:80px}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input{border:0;background-color:transparent!important;border-top-right-radius:var(--aui-border-radius-m);border-bottom-right-radius:var(--aui-border-radius-m)}:host .addon-label{white-space:nowrap}:host .input-groups-wrapper{display:flex;flex:1;align-items:center}:host .input-groups-wrapper:first-child{margin-right:8px}:host .resource_requirements-display-item{display:flex;font-size:14px;line-height:20px;color:rgb(var(--aui-color-main-text));align-items:center;margin-top:8px}:host .resource_requirements-display-item:first-child{margin-top:0}:host .resource_requirements-display-item__label{white-space:nowrap;margin-right:8px}:host .resource_requirements-display-item__value{min-width:0;flex:1;white-space:nowrap;display:flex}:host .resource_requirements-display-item__value__overflow{display:block;overflow:hidden;text-overflow:ellipsis}:host .resource_requirements-display-item__value__wrap{white-space:pre-wrap;flex-wrap:wrap}:host .resource_requirements-display-item__label,:host .resource_requirements-display-item__value{height:100%;align-items:flex-start}:host ::ng-deep .aui-input-group__addon--before{width:70px}html:not([lang|=zh]) :host ::ng-deep .memory .aui-input-group__addon--before{width:82px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: FormModule }, { kind: "directive", type: i2.FormItemControlDirective, selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2.InputComponent, selector: "input[aui-input],textarea[aui-input]", inputs: ["size", "disabled"] }, { kind: "component", type: i2.InputGroupComponent, selector: "aui-input-group" }, { kind: "directive", type: i2.InputAddonBeforeDirective, selector: "[auiInputAddonBefore]" }, { kind: "directive", type: i2.InputAddonAfterDirective, selector: "[auiInputAddonAfter]" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.SelectComponent, selector: "aui-select" }, { kind: "component", type: i2.OptionComponent, selector: "aui-option", inputs: ["label", "labelContext", "value", "disabled"] }, { kind: "ngmodule", type: IconModule }, { kind: "component", type: i2.IconComponent, selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "component", type: ErrorsMapperComponent, selector: "acl-errors-mapper", inputs: ["errors", "errorsMapper", "errorsMapperFn", "controlName", "ignoreUnknownError"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: PurePipe, name: "pure" }, { kind: "pipe", type: FieldNotAvailablePipe, name: "aclFieldNotAvailable" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1699
|
+
}
|
|
1700
|
+
__decorate([
|
|
1701
|
+
bind,
|
|
1702
|
+
__metadata("design:type", Function),
|
|
1703
|
+
__metadata("design:paramtypes", [String, String]),
|
|
1704
|
+
__metadata("design:returntype", void 0)
|
|
1705
|
+
], ResourceRequirementsComponent.prototype, "minValidator", null);
|
|
1706
|
+
__decorate([
|
|
1707
|
+
bind,
|
|
1708
|
+
__metadata("design:type", Function),
|
|
1709
|
+
__metadata("design:paramtypes", [String, String]),
|
|
1710
|
+
__metadata("design:returntype", void 0)
|
|
1711
|
+
], ResourceRequirementsComponent.prototype, "maxValidator", null);
|
|
1712
|
+
__decorate([
|
|
1713
|
+
bind,
|
|
1714
|
+
__metadata("design:type", Function),
|
|
1715
|
+
__metadata("design:paramtypes", [String, String, String, String]),
|
|
1716
|
+
__metadata("design:returntype", void 0)
|
|
1717
|
+
], ResourceRequirementsComponent.prototype, "getMinOrMaxErrorsMapper", null);
|
|
1718
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ResourceRequirementsComponent, decorators: [{
|
|
1719
|
+
type: Component,
|
|
1720
|
+
args: [{ selector: 'acl-crd-resource-requirements', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
1721
|
+
CommonModule,
|
|
1722
|
+
ReactiveFormsModule,
|
|
1723
|
+
FormModule,
|
|
1724
|
+
InputModule,
|
|
1725
|
+
SelectModule,
|
|
1726
|
+
IconModule,
|
|
1727
|
+
ErrorsMapperComponent,
|
|
1728
|
+
TranslatePipe,
|
|
1729
|
+
PurePipe,
|
|
1730
|
+
FieldNotAvailablePipe,
|
|
1731
|
+
], template: "@if (!readOnly) {\n <form [formGroup]=\"form\">\n <div class=\"resource_requirements\">\n <div class=\"input-group-label\">\n {{ 'resource_requirements_requests' | translate }}\n </div>\n <div\n class=\"input-group-wrapper tw-items-start\"\n formGroupName=\"requests\"\n >\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper cpu\"\n formGroupName=\"cpu\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': requestCpuRequired,\n }\"\n >\n CPU\n </div>\n <input\n aui-input\n type=\"number\"\n [required]=\"requestCpuRequired\"\n auiFormItemControl\n formControlName=\"value\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['cpu']; track unit) {\n <aui-option\n [value]=\"unit.value\"\n [label]=\"unit.label | translate\"\n >\n {{ unit.label | translate }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('requests.cpu.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('requests.cpu.value').errors\"\n [errorsMapper]=\"{\n min:\n requestCpuMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'cpu'\n : translate.locale,\n max:\n requestCpuMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'cpu'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper memory\"\n [formGroupName]=\"'memory'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': requestMemoryRequired,\n }\"\n >\n {{ 'memory' | translate }}\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"requestMemoryRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['memory']; track unit) {\n <aui-option [value]=\"unit\">\n {{ unit }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('requests.memory.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('requests.memory.value').errors\"\n [errorsMapper]=\"{\n min:\n requestMemoryMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'memory'\n : translate.locale,\n max:\n requestMemoryMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'memory'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n </div>\n </div>\n <div class=\"resource_requirements\">\n <div class=\"input-group-label\">\n {{ 'resource_requirements_limits' | translate }}\n </div>\n <div\n class=\"input-group-wrapper tw-items-start\"\n formGroupName=\"limits\"\n >\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper cpu\"\n [formGroupName]=\"'cpu'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': limitCpuRequired,\n }\"\n >\n CPU\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"limitCpuRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['cpu']; track unit) {\n <aui-option\n [value]=\"unit.value\"\n [label]=\"unit.label | translate\"\n >\n {{ unit.label | translate }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('limits.cpu.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('limits.cpu.value').errors\"\n [errorsMapper]=\"{\n cpu_max: 'request_greater_than_limits_error' | translate,\n min:\n limitCpuMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'cpu'\n : translate.locale,\n max:\n limitCpuMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'cpu'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-1\">\n <div\n class=\"input-groups-wrapper memory\"\n [formGroupName]=\"'memory'\"\n >\n <aui-input-group>\n <div\n auiInputAddonBefore\n class=\"addon-label\"\n [ngClass]=\"{\n 'tw-required-mark': limitMemoryRequired,\n }\"\n >\n {{ 'memory' | translate }}\n </div>\n <input\n aui-input\n type=\"number\"\n auiFormItemControl\n formControlName=\"value\"\n [required]=\"limitMemoryRequired\"\n />\n <aui-select\n auiInputAddonAfter\n auiFormItemControl\n formControlName=\"unit\"\n >\n @for (unit of resourceUnits['memory']; track unit) {\n <aui-option [value]=\"unit\">\n {{ unit }}\n </aui-option>\n }\n </aui-select>\n </aui-input-group>\n </div>\n @if (\n form.get('limits.memory.value')?.dirty ||\n $any(controlContainer?.formDirective)?.submitted\n ) {\n <acl-errors-mapper\n [errors]=\"form.get('limits.memory.value').errors\"\n [errorsMapper]=\"{\n memory_max: 'request_greater_than_limits_error' | translate,\n min:\n limitMemoryMin\n | pure\n : getMinOrMaxErrorsMapper\n : 'min'\n : 'memory'\n : translate.locale,\n max:\n limitMemoryMax\n | pure\n : getMinOrMaxErrorsMapper\n : 'max'\n : 'memory'\n : translate.locale,\n }\"\n class=\"tw-text-s tw-text-red\"\n >\n </acl-errors-mapper>\n }\n </div>\n </div>\n </div>\n </form>\n} @else {\n <div class=\"resource_requirements-display\">\n <div class=\"resource_requirements-display-item\">\n <label class=\"resource_requirements-display-item__label\">{{\n 'resource_requirements_requests' | translate\n }}</label>\n <aui-icon\n icon=\"prod:cpu\"\n size=\"16px\"\n color=\"#6359b4\"\n margin=\"right\"\n [title]=\"'cpu' | translate\"\n ></aui-icon\n ><span class=\"tw-mr-16\"\n >{{\n form.get('requests').get('cpu').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}{{\n form.get('requests').get('cpu.unit').value\n | pure: getCpuUnitLabel\n | translate\n }}\n </span>\n <aui-icon\n icon=\"prod:memory\"\n size=\"16px\"\n color=\"#007cb5\"\n margin=\"right\"\n [title]=\"'memory' | translate\"\n ></aui-icon\n >{{\n form.get('requests').get('memory').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}\n </div>\n <div class=\"resource_requirements-display-item\">\n <label class=\"resource_requirements-display-item__label\">{{\n 'resource_requirements_limits' | translate\n }}</label>\n <aui-icon\n icon=\"prod:cpu\"\n size=\"16px\"\n color=\"#6359b4\"\n margin=\"right\"\n [title]=\"'cpu' | translate\"\n ></aui-icon\n ><span class=\"tw-mr-16\"\n >{{\n form.get('limits').get('cpu').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}{{\n form.get('limits').get('cpu.unit').value\n | pure: getCpuUnitLabel\n | translate\n }}\n </span>\n <aui-icon\n icon=\"prod:memory\"\n size=\"16px\"\n color=\"#007cb5\"\n margin=\"right\"\n [title]=\"'memory' | translate\"\n ></aui-icon\n >{{\n form.get('limits').get('memory').value\n | pure: getResourceValue\n | aclFieldNotAvailable\n }}\n </div>\n </div>\n}\n", styles: [":host .resource_requirements:not(:last-of-type){margin-bottom:8px}:host .input-group-wrapper{display:flex}:host .input-group-label{line-height:var(--aui-inline-height-m)}:host aui-input-group{flex:1}:host aui-input-group ::ng-deep .aui-input-group__addon--after{padding:0}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select{width:60px}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input-group{width:62px}html:not([lang|=zh]) :host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select{width:78px}html:not([lang|=zh]) :host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input-group{width:80px}:host aui-input-group ::ng-deep .aui-input-group__addon--after aui-select .aui-input{border:0;background-color:transparent!important;border-top-right-radius:var(--aui-border-radius-m);border-bottom-right-radius:var(--aui-border-radius-m)}:host .addon-label{white-space:nowrap}:host .input-groups-wrapper{display:flex;flex:1;align-items:center}:host .input-groups-wrapper:first-child{margin-right:8px}:host .resource_requirements-display-item{display:flex;font-size:14px;line-height:20px;color:rgb(var(--aui-color-main-text));align-items:center;margin-top:8px}:host .resource_requirements-display-item:first-child{margin-top:0}:host .resource_requirements-display-item__label{white-space:nowrap;margin-right:8px}:host .resource_requirements-display-item__value{min-width:0;flex:1;white-space:nowrap;display:flex}:host .resource_requirements-display-item__value__overflow{display:block;overflow:hidden;text-overflow:ellipsis}:host .resource_requirements-display-item__value__wrap{white-space:pre-wrap;flex-wrap:wrap}:host .resource_requirements-display-item__label,:host .resource_requirements-display-item__value{height:100%;align-items:flex-start}:host ::ng-deep .aui-input-group__addon--before{width:70px}html:not([lang|=zh]) :host ::ng-deep .memory .aui-input-group__addon--before{width:82px}\n"] }]
|
|
1732
|
+
}], ctorParameters: () => [], propDecorators: { readOnly: [{
|
|
1733
|
+
type: Input
|
|
1734
|
+
}], field: [{
|
|
1735
|
+
type: Input
|
|
1736
|
+
}], minValidator: [], maxValidator: [], getMinOrMaxErrorsMapper: [] } });
|
|
1737
|
+
|
|
1738
|
+
class YamlEditorComponent extends BaseResourceFormGroupComponent {
|
|
1739
|
+
constructor() {
|
|
1740
|
+
super(inject(Injector));
|
|
1741
|
+
this.disabledActionsConfig = {};
|
|
1742
|
+
this.resultFormat = 'json';
|
|
1743
|
+
this.fieldComponent = inject(forwardRef(() => OperandFieldComponent));
|
|
1744
|
+
this.disabledActionsConfig = this.getDisabledActionsConfig(this.fieldComponent?.field);
|
|
1745
|
+
this.resultFormat = this.getResultFormat(this.fieldComponent?.field);
|
|
1746
|
+
}
|
|
1747
|
+
getResultFormat(field) {
|
|
1748
|
+
return (field?.capabilities
|
|
1749
|
+
.find(c => c.startsWith(SpecCapability.yamlResult))
|
|
1750
|
+
?.split(SpecCapability.yamlResult)?.[1] || 'json');
|
|
1751
|
+
}
|
|
1752
|
+
createForm() {
|
|
1753
|
+
return this.fb.group({
|
|
1754
|
+
yaml: null,
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
getDisabledActionsConfig(field) {
|
|
1758
|
+
const disabledActions = field?.capabilities
|
|
1759
|
+
.find(c => c.startsWith(SpecCapability.yamlActionsDisabled))
|
|
1760
|
+
?.split(SpecCapability.yamlActionsDisabled)?.[1]
|
|
1761
|
+
?.split(',') || [];
|
|
1762
|
+
return disabledActions.reduce((acc, cur) => ({ ...acc, [cur]: false }), {});
|
|
1763
|
+
}
|
|
1764
|
+
adaptResourceModel(resource) {
|
|
1765
|
+
try {
|
|
1766
|
+
return {
|
|
1767
|
+
yaml: resource
|
|
1768
|
+
? stringify(this.resultFormat === 'json'
|
|
1769
|
+
? resource
|
|
1770
|
+
: JSON.parse(resource))
|
|
1771
|
+
: '',
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
catch (e) {
|
|
1775
|
+
console.error(e);
|
|
1776
|
+
return {
|
|
1777
|
+
yaml: '',
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
adaptFormModel(formModel) {
|
|
1782
|
+
try {
|
|
1783
|
+
const parsedYaml = parse(formModel.yaml);
|
|
1784
|
+
return this.resultFormat === 'json'
|
|
1785
|
+
? parsedYaml
|
|
1786
|
+
: parsedYaml
|
|
1787
|
+
? JSON.stringify(parsedYaml)
|
|
1788
|
+
: '';
|
|
1789
|
+
}
|
|
1790
|
+
catch { }
|
|
1791
|
+
return null;
|
|
1792
|
+
}
|
|
1793
|
+
getActionsConfig(defaultActions, disabledActions) {
|
|
1794
|
+
return {
|
|
1795
|
+
...defaultActions,
|
|
1796
|
+
...disabledActions,
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: YamlEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1800
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: YamlEditorComponent, isStandalone: true, selector: "acl-crd-yaml-editor", inputs: { actionsConfig: "actionsConfig", options: "options" }, usesInheritance: true, ngImport: i0, template: "<form\n aui-form\n [formGroup]=\"form\"\n>\n <aui-code-editor\n auiFormItemControl\n formControlName=\"yaml\"\n [actionsConfig]=\"\n actionsConfig | pure: getActionsConfig : disabledActionsConfig\n \"\n [options]=\"options\"\n ></aui-code-editor>\n</form>\n", styles: [":host{width:100%}:host form{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormModule }, { kind: "directive", type: i2.FormItemControlDirective, selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "component", type: CodeEditorComponent, selector: "aui-code-editor", inputs: ["options", "plain", "showLanguageLabel", "value", "originalValue", "actionsConfig", "previewMode", "diffMode", "modelUri"], outputs: ["editorChange", "editorBlur"] }, { kind: "pipe", type: PurePipe, name: "pure" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1801
|
+
}
|
|
1802
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: YamlEditorComponent, decorators: [{
|
|
1803
|
+
type: Component,
|
|
1804
|
+
args: [{ selector: 'acl-crd-yaml-editor', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ReactiveFormsModule, FormModule, CodeEditorComponent, PurePipe], template: "<form\n aui-form\n [formGroup]=\"form\"\n>\n <aui-code-editor\n auiFormItemControl\n formControlName=\"yaml\"\n [actionsConfig]=\"\n actionsConfig | pure: getActionsConfig : disabledActionsConfig\n \"\n [options]=\"options\"\n ></aui-code-editor>\n</form>\n", styles: [":host{width:100%}:host form{width:100%}\n"] }]
|
|
1805
|
+
}], ctorParameters: () => [], propDecorators: { actionsConfig: [{
|
|
1806
|
+
type: Input
|
|
1807
|
+
}], options: [{
|
|
1808
|
+
type: Input
|
|
1809
|
+
}] } });
|
|
1810
|
+
|
|
1811
|
+
const PASSWORD_REVEAL = '******';
|
|
1812
|
+
const ENCODING_MAPPER = {
|
|
1813
|
+
base64: {
|
|
1814
|
+
decode,
|
|
1815
|
+
encode,
|
|
1816
|
+
},
|
|
1817
|
+
};
|
|
1818
|
+
class OperandFieldComponent {
|
|
1819
|
+
constructor() {
|
|
1820
|
+
this.readonly = false;
|
|
1821
|
+
this.valueChange = new EventEmitter();
|
|
1822
|
+
this.itemRemove = new EventEmitter();
|
|
1823
|
+
this.control = new FormControl();
|
|
1824
|
+
this.onDestroy$ = new Subject();
|
|
1825
|
+
this.getFieldType = getFieldType;
|
|
1826
|
+
this.isLink = isLink;
|
|
1827
|
+
this.SpecCapability = SpecCapability;
|
|
1828
|
+
this.getFieldDisplayName = getFieldDisplayName;
|
|
1829
|
+
this.getFieldDescription = getFieldDescription$1;
|
|
1830
|
+
this.getFieldPlaceholder = getFieldPlaceholder$1;
|
|
1831
|
+
this.getRadioOptions = getRadioOptions;
|
|
1832
|
+
this.editorOptions = yamlWriteOptions;
|
|
1833
|
+
this.editorActions = createActions;
|
|
1834
|
+
this.isGroupField = isGroupField;
|
|
1835
|
+
this.isArrayField = isArrayField;
|
|
1836
|
+
this.isArrayFieldTable = isArrayFieldTable;
|
|
1837
|
+
this.convertBooleanSwitchValue = convertBooleanSwitchValue;
|
|
1838
|
+
this.loading$ = new BehaviorSubject(false);
|
|
1839
|
+
this.PASSWORD_REVEAL = PASSWORD_REVEAL;
|
|
1840
|
+
this.isBasicTypeField = (filed) => ![
|
|
1841
|
+
SpecCapability.object,
|
|
1842
|
+
SpecCapability.array,
|
|
1843
|
+
SpecCapability.password,
|
|
1844
|
+
SpecCapability.confirmPassword,
|
|
1845
|
+
SpecCapability.externalPassword,
|
|
1846
|
+
].includes(getFieldType(filed));
|
|
1847
|
+
this.viewActions = viewActions;
|
|
1848
|
+
this.viewOptions = yamlReadOptions;
|
|
1849
|
+
this.crdForm = inject(CrdFormComponent);
|
|
1850
|
+
this.http = inject(HttpClient);
|
|
1851
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
1852
|
+
this.getWidgets = (capabilities) => {
|
|
1853
|
+
const descriptor = capabilities.find(c => c.startsWith(SpecCapability.widgets));
|
|
1854
|
+
return descriptor
|
|
1855
|
+
? this.crdForm.widgets?.find(w => w.descriptor === descriptor.split('?=')[0])
|
|
1856
|
+
: null;
|
|
1857
|
+
};
|
|
1858
|
+
this.dynamicOptionRefresh$$ = new BehaviorSubject(null);
|
|
1859
|
+
this.dynamicOptionRefreshDisabled$$ = new BehaviorSubject(false);
|
|
1860
|
+
this.dynamicOptionLoading$$ = new Subject();
|
|
1861
|
+
this.dynamicExpression$ = this.field$.pipe(map$1(field => {
|
|
1862
|
+
return new PropsFieldExpression(field).expressions;
|
|
1863
|
+
}), publishRef());
|
|
1864
|
+
this.dynamicOptions$ = this.field$.pipe(switchMap(field => {
|
|
1865
|
+
const dynamicExpression = new PropsFieldExpression(field).expressions;
|
|
1866
|
+
if (!dynamicExpression.options) {
|
|
1867
|
+
return of(this.getSelectOptions(field).map(value => ({
|
|
1868
|
+
label: value,
|
|
1869
|
+
value,
|
|
1870
|
+
})));
|
|
1871
|
+
}
|
|
1872
|
+
return combineLatest([
|
|
1873
|
+
this.dynamicOptionRefresh$$,
|
|
1874
|
+
combineLatest([this.crdForm.formContext$, this.formDataState$]).pipe(debounceTime(100), distinctUntilChanged((p, n) => compareWithSubscribedPaths(dynamicExpression, p, n)), tap(([formContext, formDataState]) => {
|
|
1875
|
+
if (this.crdForm.debug) {
|
|
1876
|
+
/* eslint-disable no-console */
|
|
1877
|
+
console.log('field: ', field, 'formContext: ', formContext, 'formDataState: ', formDataState.toJS());
|
|
1878
|
+
}
|
|
1879
|
+
})),
|
|
1880
|
+
]).pipe(map$1(([, [formContext, formDataState]]) => [formContext, formDataState]), tap(() => {
|
|
1881
|
+
this.dynamicOptionLoading$$.next(true);
|
|
1882
|
+
}), switchMap(([formContext, formDataState]) => {
|
|
1883
|
+
let result = [];
|
|
1884
|
+
const { api, labelPath, valuePath, expression } = dynamicExpression.options;
|
|
1885
|
+
const stringExpressionContext = this.getStringExpressionContext(formContext, formDataState);
|
|
1886
|
+
// console.log('api: ', api);
|
|
1887
|
+
const url = template(api)(stringExpressionContext);
|
|
1888
|
+
this.dynamicOptionRefreshDisabled$$.next(url.includes('//'));
|
|
1889
|
+
if (!api && expression) {
|
|
1890
|
+
result = evalInContext(expression, stringExpressionContext);
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
return stringExpressionContext.fetchResourceList(url, (item) => {
|
|
1894
|
+
const value = get(item, valuePath, '');
|
|
1895
|
+
return {
|
|
1896
|
+
label: get(item, labelPath, value),
|
|
1897
|
+
value,
|
|
1898
|
+
};
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
return isObservable(result) ? result : of(result);
|
|
1902
|
+
}), tap(() => {
|
|
1903
|
+
this.dynamicOptionLoading$$.next(false);
|
|
1904
|
+
}));
|
|
1905
|
+
}), publishRef());
|
|
1906
|
+
this.staticDefaultValue$ = this.field$.pipe(switchMap(field => {
|
|
1907
|
+
const defaultValueExpression = new PropsFieldExpression(field)
|
|
1908
|
+
.expressions;
|
|
1909
|
+
if (!defaultValueExpression.default ||
|
|
1910
|
+
this.getFieldCurrentValue() !== undefined) {
|
|
1911
|
+
return of({
|
|
1912
|
+
value: this.getFieldValue(),
|
|
1913
|
+
isDynamic: false,
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
return combineLatest([
|
|
1917
|
+
this.crdForm.formContext$,
|
|
1918
|
+
this.formDataState$,
|
|
1919
|
+
]).pipe(distinctUntilChanged((p, n) => compareWithSubscribedPaths(defaultValueExpression, p, n)), map$1(([formContext, formDataState]) => template(defaultValueExpression.default)(this.getStringExpressionContext(formContext, formDataState))), map$1(value => ({
|
|
1920
|
+
value,
|
|
1921
|
+
isDynamic: true,
|
|
1922
|
+
})));
|
|
1923
|
+
}), publishRef());
|
|
1924
|
+
this.dynamicHiddenValue$ = this.field$.pipe(switchMap(field => {
|
|
1925
|
+
const { expressions } = new PropsFieldExpression(field);
|
|
1926
|
+
if (!expressions.hidden) {
|
|
1927
|
+
return of(false);
|
|
1928
|
+
}
|
|
1929
|
+
return combineLatest([
|
|
1930
|
+
this.crdForm.formContext$,
|
|
1931
|
+
this.formDataState$,
|
|
1932
|
+
]).pipe(distinctUntilChanged((p, n) => compareWithSubscribedPaths(expressions, p, n)), map$1(([formContext, formDataState]) => template(expressions.hidden)(this.getStringExpressionContext(formContext, formDataState)) === 'true'));
|
|
1933
|
+
}));
|
|
1934
|
+
this.getStringExpressionContext = (formContext, formDataState) => ({
|
|
1935
|
+
fetchResourceList: this.fetchResourceList,
|
|
1936
|
+
context: formContext || {},
|
|
1937
|
+
formData: formDataState.toJS()?.spec || {},
|
|
1938
|
+
});
|
|
1939
|
+
this.fetchResourceList = (url, callback) => {
|
|
1940
|
+
// 例如 '/kubernetes/global/api/v1/namespaces//secrets' 表示 formData.namespace 没有插值
|
|
1941
|
+
if (url.includes('//')) {
|
|
1942
|
+
return of([]);
|
|
1943
|
+
}
|
|
1944
|
+
this.loading$.next(true);
|
|
1945
|
+
return this.http
|
|
1946
|
+
.get(`${API_GATEWAY}${url}`)
|
|
1947
|
+
.pipe(map$1(res => res.items.map(item => callback.call(this, item))), skipError([]), finalize(() => {
|
|
1948
|
+
this.loading$.next(false);
|
|
1949
|
+
}));
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
get isDisabled() {
|
|
1953
|
+
return this.readonly || this.field?.disabled;
|
|
1954
|
+
}
|
|
1955
|
+
get effectControlDefaultValue() {
|
|
1956
|
+
return this.crdForm.effectControlDefaultValue;
|
|
1957
|
+
}
|
|
1958
|
+
get isRequired() {
|
|
1959
|
+
return isRequired(this.field);
|
|
1960
|
+
}
|
|
1961
|
+
get isCreatable() {
|
|
1962
|
+
return isCreatable(this.field);
|
|
1963
|
+
}
|
|
1964
|
+
get isMultiple() {
|
|
1965
|
+
return isMultiple(this.field);
|
|
1966
|
+
}
|
|
1967
|
+
get isAllowCreate() {
|
|
1968
|
+
return isAllowCreate(this.field);
|
|
1969
|
+
}
|
|
1970
|
+
get isClearable() {
|
|
1971
|
+
return isClearable(this.field);
|
|
1972
|
+
}
|
|
1973
|
+
// 通过hidden属性隐藏不必要的元素,和 aclScrollToFirstInvalid 指令过滤隐藏元素保持一致
|
|
1974
|
+
get isHidden() {
|
|
1975
|
+
return ((this.hasFieldDependency() && !this.checkFieldDependency(this.field)) ||
|
|
1976
|
+
this._hidden);
|
|
1977
|
+
}
|
|
1978
|
+
set isHidden(hidden) {
|
|
1979
|
+
this._hidden = hidden;
|
|
1980
|
+
this.cdr.markForCheck();
|
|
1981
|
+
}
|
|
1982
|
+
get isCustomErrorsMapper() {
|
|
1983
|
+
return getFieldType(this.field) === SpecCapability.resourceRequirements;
|
|
1984
|
+
}
|
|
1985
|
+
registerControl() {
|
|
1986
|
+
this.crdForm.controlMap.set(getOperandPath(this.field), this.control);
|
|
1987
|
+
}
|
|
1988
|
+
unRegisterControl() {
|
|
1989
|
+
this.crdForm.controlMap.delete(getOperandPath(this.field));
|
|
1990
|
+
}
|
|
1991
|
+
ngOnInit() {
|
|
1992
|
+
this.dynamicHiddenValue$.subscribe(value => {
|
|
1993
|
+
this.isHidden = value;
|
|
1994
|
+
});
|
|
1995
|
+
this.registerControl();
|
|
1996
|
+
if (this.field.validations) {
|
|
1997
|
+
const validators = map(this.field.validations, (val, rule) => {
|
|
1998
|
+
switch (rule) {
|
|
1999
|
+
case Validations.maximum: {
|
|
2000
|
+
return Validators.max(val);
|
|
2001
|
+
}
|
|
2002
|
+
case Validations.minimum: {
|
|
2003
|
+
return Validators.min(val);
|
|
2004
|
+
}
|
|
2005
|
+
case Validations.required: {
|
|
2006
|
+
return Validators.required;
|
|
2007
|
+
}
|
|
2008
|
+
case Validations.maxLength: {
|
|
2009
|
+
return Validators.maxLength(val);
|
|
2010
|
+
}
|
|
2011
|
+
case Validations.minLength: {
|
|
2012
|
+
return Validators.minLength(val);
|
|
2013
|
+
}
|
|
2014
|
+
case Validations.pattern: {
|
|
2015
|
+
return Validators.pattern(val);
|
|
2016
|
+
}
|
|
2017
|
+
default: {
|
|
2018
|
+
return null;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}).filter(v => !!v);
|
|
2022
|
+
if (validators.length) {
|
|
2023
|
+
this.control.addValidators((this._validators = validators));
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
// 将 valueChanges 的逻辑移动到 ngAfterViewInit 中,主要是为了避免全局 ValidatorsDirective带来的影响,需要等到ValidatorsDirective的ngAfterViewInit之后再订阅control的valueChanges
|
|
2028
|
+
ngAfterViewInit() {
|
|
2029
|
+
this.control.valueChanges
|
|
2030
|
+
.pipe(startWithCondition(this.effectControlDefaultValue, this.control.value), debounceTime(50), distinctUntilChanged(), takeUntil(this.onDestroy$), filter(v =>
|
|
2031
|
+
// FIXME: This is hack. yaml value will only be null if format error(because try-catch), the data flow now is from control => formDataState => control(will judge equality),this will make control be empty
|
|
2032
|
+
getFieldType(this.field) === SpecCapability.yaml ? v !== null : true))
|
|
2033
|
+
.subscribe(value => {
|
|
2034
|
+
this.valueChange.emit({
|
|
2035
|
+
field: this.field,
|
|
2036
|
+
data: value,
|
|
2037
|
+
errors: this.control.errors,
|
|
2038
|
+
});
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
ngOnDestroy() {
|
|
2042
|
+
this.onDestroy$.next();
|
|
2043
|
+
this.onDestroy$.complete();
|
|
2044
|
+
this.unRegisterControl();
|
|
2045
|
+
if (this._validators?.length) {
|
|
2046
|
+
this.control.removeValidators(this._validators);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
ngOnChanges({ formDataState }) {
|
|
2050
|
+
if (this.field && formDataState?.firstChange) {
|
|
2051
|
+
this.staticDefaultValue$
|
|
2052
|
+
.pipe(take(1), filter(({ value }) => value !== undefined))
|
|
2053
|
+
.subscribe(({ value, isDynamic }) => {
|
|
2054
|
+
this.control.setValue(value, { emitEvent: isDynamic });
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
else if (formDataState?.currentValue) {
|
|
2058
|
+
const currentValue = getFormData(formDataState.currentValue, this.field.path, false);
|
|
2059
|
+
const prevValue = getFormData(formDataState.previousValue, this.field.path, false);
|
|
2060
|
+
const rawValue = isImmutable(currentValue)
|
|
2061
|
+
? currentValue.toJS()
|
|
2062
|
+
: currentValue;
|
|
2063
|
+
if (!isEqual(rawValue, prevValue) &&
|
|
2064
|
+
!isEqual(rawValue, this.control.value)) {
|
|
2065
|
+
this.control.setValue(rawValue);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
getFieldCurrentValue() {
|
|
2070
|
+
let value = getFormData(this.formDataState, this.field.path, false);
|
|
2071
|
+
// 如果值是 Immutable object 类型,需要转成原始 JS Object
|
|
2072
|
+
if (isImmutable(value)) {
|
|
2073
|
+
value = value.toJS();
|
|
2074
|
+
}
|
|
2075
|
+
return value;
|
|
2076
|
+
}
|
|
2077
|
+
getFieldValue(useDefault = true) {
|
|
2078
|
+
const currentValue = this.getFieldCurrentValue();
|
|
2079
|
+
if (currentValue === undefined && useDefault) {
|
|
2080
|
+
return getDefaultValue(this.field);
|
|
2081
|
+
}
|
|
2082
|
+
return currentValue;
|
|
2083
|
+
}
|
|
2084
|
+
getOptionDisplay(value, options) {
|
|
2085
|
+
return options.find(item => item.value === value)?.display;
|
|
2086
|
+
}
|
|
2087
|
+
getSelectOptions(field) {
|
|
2088
|
+
return field.capabilities
|
|
2089
|
+
.filter(c => c.startsWith(SpecCapability.select))
|
|
2090
|
+
.map(c => c.split(SpecCapability.select)?.[1]);
|
|
2091
|
+
}
|
|
2092
|
+
getEncoding(field, type) {
|
|
2093
|
+
const prefix = `urn:alm:descriptor:${type}:encoding:`;
|
|
2094
|
+
const capability = field.capabilities.find(it => it.startsWith(prefix));
|
|
2095
|
+
let encoding;
|
|
2096
|
+
if (capability) {
|
|
2097
|
+
encoding = ENCODING_MAPPER[capability.slice(prefix.length)];
|
|
2098
|
+
}
|
|
2099
|
+
return (encoding || {
|
|
2100
|
+
encode: identity,
|
|
2101
|
+
decode: identity,
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
hasFieldDependency() {
|
|
2105
|
+
return this.field.capabilities.some(c => c.startsWith(SpecCapability.fieldDependency));
|
|
2106
|
+
}
|
|
2107
|
+
checkFieldDependency(field) {
|
|
2108
|
+
try {
|
|
2109
|
+
const dependencies = field?.capabilities
|
|
2110
|
+
.filter(c => c.startsWith(SpecCapability.fieldDependency))
|
|
2111
|
+
.map(i => i.split(SpecCapability.fieldDependency)[1]);
|
|
2112
|
+
if (!dependencies?.length) {
|
|
2113
|
+
return true;
|
|
2114
|
+
}
|
|
2115
|
+
const parentPaths = dependencies.map(d => d.split(':'));
|
|
2116
|
+
const state = parentPaths.every(([path, ...values]) => {
|
|
2117
|
+
// Support depend one of values
|
|
2118
|
+
const dependencyField = this.fields.find(f => f.path === normalizePath(path));
|
|
2119
|
+
const dependencyDefaultValue = getDefaultValue(dependencyField);
|
|
2120
|
+
const formStateValue = getFormData(this.formDataState, path);
|
|
2121
|
+
const currentValue = formStateValue === null || formStateValue === undefined
|
|
2122
|
+
? dependencyDefaultValue
|
|
2123
|
+
: formStateValue;
|
|
2124
|
+
return values.some(val => convertValue(val, dependencyField.type) === currentValue);
|
|
2125
|
+
});
|
|
2126
|
+
/**
|
|
2127
|
+
* 父 field dependency 不满足时,即隐藏时,本 field 也应当隐藏。
|
|
2128
|
+
* 因为在大部分情形下,fieldDependency 与 oneOf 搭配来描述一组关联关系,很少只有 b 仅依赖于 a,当 a 隐藏后,还要根据 a 的默认值要求显示 b 的情况,
|
|
2129
|
+
* 对于该情况,可以多加一个配备了额外 fieldDependency 的 Descriptor 来描述 b
|
|
2130
|
+
*/
|
|
2131
|
+
return (parentPaths.every(([path]) => this.checkFieldDependency(this.fields.find(i => i.path === path))) && state);
|
|
2132
|
+
}
|
|
2133
|
+
catch (error) {
|
|
2134
|
+
console.error(error);
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2139
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: OperandFieldComponent, isStandalone: true, selector: "acl-operand-field", inputs: { field: "field", readonly: "readonly", fields: "fields", formDataState: "formDataState", formErrors: "formErrors" }, outputs: { valueChange: "valueChange", itemRemove: "itemRemove" }, host: { properties: { "hidden": "this.isHidden" } }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"field\">\n <ng-container\n *ngIf=\"{\n fieldType: field | pure: getFieldType,\n isBasicTypeField: field | pure: isBasicTypeField,\n } as meta\"\n >\n <ng-container *ngIf=\"meta.isBasicTypeField; else advancedFiled\">\n <acl-crd-form-item\n *ngIf=\"!(field.capabilities | pure: getWidgets)\"\n [ngSwitch]=\"meta.fieldType\"\n >\n <input\n *ngSwitchCase=\"SpecCapability.number\"\n type=\"number\"\n aui-input\n auiFormItemControl\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n />\n <ng-container *ngSwitchCase=\"SpecCapability.booleanSwitch\">\n <aui-switch\n auiFormItemControl\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"switchReadonly\"\n [formControl]=\"control\"\n ></aui-switch>\n <ng-template #switchReadonly>{{\n control.value\n | pure: convertBooleanSwitchValue : field.capabilities\n | translate\n }}</ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.select\">\n <aui-multi-select\n *ngIf=\"isMultiple\"\n [maxRowCount]=\"3\"\n auiFormItemControl\n [clearable]=\"true\"\n [filterable]=\"true\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [loading]=\"loading$ | async\"\n [allowCreate]=\"isAllowCreate\"\n >\n <aui-option\n *ngFor=\"let option of dynamicOptions$ | async\"\n [value]=\"option.value\"\n [label]=\"option.label\"\n >\n {{ option.label }}\n </aui-option>\n <aui-option-placeholder>{{\n 'no_data' | translate\n }}</aui-option-placeholder>\n </aui-multi-select>\n\n <aui-select\n *ngIf=\"!isMultiple\"\n auiFormItemControl\n [clearable]=\"true\"\n [filterable]=\"true\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [loading]=\"loading$ | async\"\n [allowCreate]=\"isAllowCreate\"\n >\n <aui-option\n *ngFor=\"let option of dynamicOptions$ | async\"\n [value]=\"option.value\"\n [label]=\"option.label | aclParseJsonTranslate\"\n >\n {{ option.label | aclParseJsonTranslate }}\n </aui-option>\n <aui-option-placeholder>{{\n 'no_data' | translate\n }}</aui-option-placeholder>\n </aui-select>\n\n <button\n *ngIf=\"(dynamicExpression$ | async).options\"\n style=\"margin-left: 4px\"\n aui-button\n type=\"button\"\n [square]=\"true\"\n [disabled]=\"\n (dynamicOptionRefreshDisabled$$ | async) ||\n (dynamicOptionLoading$$ | async)\n \"\n [loading]=\"dynamicOptionLoading$$ | async\"\n (click)=\"dynamicOptionRefresh$$.next()\"\n >\n <aui-icon icon=\"rotate_right\"></aui-icon>\n </button>\n </ng-container>\n <aui-radio-group\n *ngSwitchCase=\"SpecCapability.radio\"\n auiFormItemControl\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"radioLabel\"\n >\n <ng-container>\n <aui-radio-button\n *ngFor=\"let option of field.capabilities | pure: getRadioOptions\"\n [value]=\"option.value\"\n >\n {{ option.display | translate }}\n </aui-radio-button>\n <ng-template #radioLabel>\n {{\n control.value\n | pure\n : getOptionDisplay\n : (field.capabilities | pure: getRadioOptions)\n | translate\n }}\n </ng-template>\n </ng-container>\n </aui-radio-group>\n <ng-container *ngSwitchCase=\"SpecCapability.resourceRequirements\">\n <acl-crd-resource-requirements\n auiFormItemControl\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"resourceRequirementsReadonly\"\n ></acl-crd-resource-requirements>\n <ng-template #resourceRequirementsReadonly>\n <acl-crd-resource-requirements\n auiFormItemControl\n [ngModel]=\"control.value\"\n [readOnly]=\"true\"\n ></acl-crd-resource-requirements>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.yaml\">\n <acl-crd-yaml-editor\n auiFormItemControl\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [actionsConfig]=\"editorActions\"\n [options]=\"editorOptions\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"yamlReadonly\"\n ></acl-crd-yaml-editor>\n <ng-template #yamlReadonly>\n <acl-crd-yaml-editor\n auiFormItemControl\n [ngModel]=\"control.value\"\n [actionsConfig]=\"viewActions\"\n [options]=\"viewOptions\"\n ></acl-crd-yaml-editor>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.k8sResourcePrefix\">\n <acl-crd-resource-prefix\n auiFormItemControl\n [formErrors]=\"control.errors\"\n [field]=\"field\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"resourcePrefixReadonly\"\n ></acl-crd-resource-prefix>\n <ng-template #resourcePrefixReadonly>\n <acl-crd-resource-prefix\n auiFormItemControl\n [field]=\"field\"\n [ngModel]=\"control.value\"\n [readonly]=\"true\"\n ></acl-crd-resource-prefix>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.basicAuthSecret\">\n <acl-crd-basic-auth-secret\n auiFormItemControl\n [field]=\"field\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n ></acl-crd-basic-auth-secret>\n </ng-container>\n <textarea\n *ngSwitchCase=\"SpecCapability.textarea\"\n auiFormItemControl\n aui-input\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n ></textarea>\n <aui-tags-input\n *ngSwitchCase=\"SpecCapability.tagsInput\"\n auiFormItemControl\n class=\"tw-flex-1\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n [aclReadonlyField]=\"isDisabled\"\n [clearable]=\"isClearable\"\n ></aui-tags-input>\n <ng-container *ngSwitchDefault>\n <acl-crd-link\n *ngIf=\"(field | pure: isLink) && isDisabled; else textField\"\n auiFormItemControl\n [field]=\"field\"\n [formControl]=\"control\"\n ></acl-crd-link>\n </ng-container>\n <ng-template #textField>\n <input\n type=\"text\"\n auiFormItemControl\n [required]=\"isRequired\"\n aui-input\n [formControl]=\"control\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n [aclReadonlyField]=\"isDisabled\"\n />\n </ng-template>\n </acl-crd-form-item>\n <div\n class=\"tw-flex tw-w-full tw-flex-col\"\n *ngIf=\"field.capabilities | pure: getWidgets as widget\"\n >\n <ng-container *ngComponentOutlet=\"widget.component\"></ng-container>\n </div>\n </ng-container>\n <ng-template #advancedFiled>\n <ng-container\n *ngIf=\"\n meta.fieldType === SpecCapability.password ||\n meta.fieldType === SpecCapability.confirmPassword ||\n meta.fieldType === SpecCapability.externalPassword\n \"\n >\n <acl-crd-form-item\n *ngIf=\"isDisabled\"\n [plain]=\"true\"\n >\n <ng-container *ngTemplateOutlet=\"passwordView\"></ng-container>\n </acl-crd-form-item>\n <acl-password-input\n *ngIf=\"!isDisabled\"\n [initPassword]=\"control.value\"\n [label]=\"field | pure: getFieldDisplayName | translate\"\n [hint]=\"field | pure: getFieldDescription | translate\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [readonly]=\"crdForm.readonly\"\n [isUpdate]=\"!!control.value && !control.dirty\"\n [strongPassword]=\"meta.fieldType !== SpecCapability.externalPassword\"\n [enableConfirm]=\"meta.fieldType === SpecCapability.confirmPassword\"\n width=\"large\"\n [encoding]=\"field | pure: getEncoding : 'password'\"\n [pattern]=\"field.validations?.pattern\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n ></acl-password-input>\n </ng-container>\n <!-- group field -->\n <ng-container *ngIf=\"isGroupField(field) && field.fieldList?.length > 0\">\n <acl-operand-field-group\n [fields]=\"fields\"\n [fieldGroup]=\"field\"\n [readonly]=\"isDisabled\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-operand-field-group>\n </ng-container>\n <!-- array field groups -->\n <!-- array field table -->\n <ng-container *ngIf=\"isArrayField(field)\">\n <acl-crd-array-table\n *ngIf=\"field | pure: isArrayFieldTable; else defaultArrayFiled\"\n [arrayFieldGroup]=\"field\"\n [formDataState]=\"formDataState\"\n [readonly]=\"isDisabled\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-crd-array-table>\n <ng-template #defaultArrayFiled>\n <acl-operand-array-field-group\n [arrayFieldGroup]=\"field\"\n [fields]=\"fields\"\n [readonly]=\"isDisabled\"\n [formDataState]=\"formDataState\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-operand-array-field-group>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-container>\n</ng-container>\n\n<ng-template #passwordView>\n {{ PASSWORD_REVEAL }}\n</ng-template>\n", styles: [":host .input-group{width:100%}:host ::ng-deep .aui-form-item__label-wrapper:not(.hasLabel){display:none}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgComponentOutlet), selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgForOf), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgIf), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgTemplateOutlet), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgSwitch), selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgSwitchCase), selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgSwitchDefault), selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: i0.forwardRef(() => FormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1.DefaultValueAccessor), selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i0.forwardRef(() => i1.NumberValueAccessor), selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i1.RequiredValidator), selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i0.forwardRef(() => i1.PatternValidator), selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i0.forwardRef(() => i1.NgModel), selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1.FormControlDirective), selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: i0.forwardRef(() => FormModule) }, { kind: "directive", type: i0.forwardRef(() => i2.FormItemControlDirective), selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "ngmodule", type: i0.forwardRef(() => InputModule) }, { kind: "component", type: i0.forwardRef(() => i2.InputComponent), selector: "input[aui-input],textarea[aui-input]", inputs: ["size", "disabled"] }, { kind: "component", type: i0.forwardRef(() => i2.TagsInputComponent), selector: "aui-tags-input", inputs: ["placeholder", "size", "clearable", "allowRepeat", "allowEmpty", "readonlyTags", "maxRowCount", "customRowHeight", "inputValidator", "inputAsyncValidator"] }, { kind: "ngmodule", type: i0.forwardRef(() => SelectModule) }, { kind: "component", type: i0.forwardRef(() => i2.SelectComponent), selector: "aui-select" }, { kind: "component", type: i0.forwardRef(() => i2.OptionComponent), selector: "aui-option", inputs: ["label", "labelContext", "value", "disabled"] }, { kind: "component", type: i0.forwardRef(() => i2.OptionPlaceholderComponent), selector: "aui-option-placeholder" }, { kind: "component", type: i0.forwardRef(() => i2.MultiSelectComponent), selector: "aui-multi-select", inputs: ["tagClassFn", "maxRowCount", "customRowHeight", "allowSelectAll"] }, { kind: "ngmodule", type: i0.forwardRef(() => RadioModule) }, { kind: "component", type: i0.forwardRef(() => i2.RadioGroupComponent), selector: "aui-radio-group", inputs: ["size", "direction", "plain", "name"] }, { kind: "component", type: i0.forwardRef(() => i2.RadioButtonComponent), selector: "aui-radio-button" }, { kind: "ngmodule", type: i0.forwardRef(() => SwitchModule) }, { kind: "component", type: i0.forwardRef(() => i2.SwitchComponent), selector: "aui-switch", inputs: ["loading"] }, { kind: "ngmodule", type: i0.forwardRef(() => TagModule) }, { kind: "ngmodule", type: i0.forwardRef(() => IconModule) }, { kind: "component", type: i0.forwardRef(() => i2.IconComponent), selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "ngmodule", type: i0.forwardRef(() => ButtonModule) }, { kind: "component", type: i0.forwardRef(() => i2.ButtonComponent), selector: "button[aui-button]", inputs: ["aui-button", "size", "plain", "loading", "round", "square"] }, { kind: "ngmodule", type: i0.forwardRef(() => TooltipModule) }, { kind: "component", type: i0.forwardRef(() => FormItemComponent), selector: "acl-crd-form-item", inputs: ["plain"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldGroupComponent), selector: "acl-operand-field-group", inputs: ["fields", "fieldGroup", "formDataState", "formErrors", "readonly"], outputs: ["valueChange"] }, { kind: "component", type: i0.forwardRef(() => OperandArrayFieldGroupComponent), selector: "acl-operand-array-field-group" }, { kind: "component", type: i0.forwardRef(() => CrdFormArrayTableComponent), selector: "acl-crd-array-table" }, { kind: "component", type: i0.forwardRef(() => ResourceRequirementsComponent), selector: "acl-crd-resource-requirements", inputs: ["readOnly", "field"] }, { kind: "component", type: i0.forwardRef(() => YamlEditorComponent), selector: "acl-crd-yaml-editor", inputs: ["actionsConfig", "options"] }, { kind: "component", type: i0.forwardRef(() => K8sResourcePrefixComponent), selector: "acl-crd-resource-prefix", inputs: ["field", "readonly", "formErrors"] }, { kind: "component", type: i0.forwardRef(() => BasicAuthSecretComponent), selector: "acl-crd-basic-auth-secret", inputs: ["required", "field"] }, { kind: "component", type: i0.forwardRef(() => LinkComponent), selector: "acl-crd-link", inputs: ["field"] }, { kind: "component", type: i0.forwardRef(() => PasswordInputComponent), selector: "acl-password-input", inputs: ["initPassword", "label", "hint", "specialChars", "strongPassword", "enableConfirm", "width", "toggleable", "pattern", "encoding", "placeholder", "readonly", "isUpdate", "required"] }, { kind: "directive", type: i0.forwardRef(() => ReadonlyFieldDirective), selector: "[aclReadonlyField]", inputs: ["aclReadonlyField", "hidden", "aclReadonlyFieldTemplate", "aclReadonlyFieldTemplateContext"] }, { kind: "pipe", type: i0.forwardRef(() => i1$1.AsyncPipe), name: "async" }, { kind: "pipe", type: i0.forwardRef(() => ParseJsonTranslatePipe), name: "aclParseJsonTranslate" }, { kind: "pipe", type: i0.forwardRef(() => PurePipe), name: "pure" }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2140
|
+
}
|
|
2141
|
+
__decorate([
|
|
2142
|
+
ObservableInput(),
|
|
2143
|
+
__metadata("design:type", Observable)
|
|
2144
|
+
], OperandFieldComponent.prototype, "field$", void 0);
|
|
2145
|
+
__decorate([
|
|
2146
|
+
ObservableInput(),
|
|
2147
|
+
__metadata("design:type", Observable)
|
|
2148
|
+
], OperandFieldComponent.prototype, "formDataState$", void 0);
|
|
2149
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandFieldComponent, decorators: [{
|
|
2150
|
+
type: Component,
|
|
2151
|
+
args: [{ selector: 'acl-operand-field', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
2152
|
+
CommonModule,
|
|
2153
|
+
FormsModule,
|
|
2154
|
+
ReactiveFormsModule,
|
|
2155
|
+
FormModule,
|
|
2156
|
+
InputModule,
|
|
2157
|
+
SelectModule,
|
|
2158
|
+
RadioModule,
|
|
2159
|
+
SwitchModule,
|
|
2160
|
+
TagModule,
|
|
2161
|
+
IconModule,
|
|
2162
|
+
ButtonModule,
|
|
2163
|
+
TooltipModule,
|
|
2164
|
+
forwardRef(() => FormItemComponent),
|
|
2165
|
+
forwardRef(() => OperandFieldGroupComponent),
|
|
2166
|
+
forwardRef(() => OperandArrayFieldGroupComponent),
|
|
2167
|
+
forwardRef(() => CrdFormArrayTableComponent),
|
|
2168
|
+
forwardRef(() => ResourceRequirementsComponent),
|
|
2169
|
+
forwardRef(() => YamlEditorComponent),
|
|
2170
|
+
K8sResourcePrefixComponent,
|
|
2171
|
+
BasicAuthSecretComponent,
|
|
2172
|
+
LinkComponent,
|
|
2173
|
+
PasswordInputComponent,
|
|
2174
|
+
ReadonlyFieldDirective,
|
|
2175
|
+
ParseJsonTranslatePipe,
|
|
2176
|
+
PurePipe,
|
|
2177
|
+
TranslatePipe,
|
|
2178
|
+
], template: "<ng-container *ngIf=\"field\">\n <ng-container\n *ngIf=\"{\n fieldType: field | pure: getFieldType,\n isBasicTypeField: field | pure: isBasicTypeField,\n } as meta\"\n >\n <ng-container *ngIf=\"meta.isBasicTypeField; else advancedFiled\">\n <acl-crd-form-item\n *ngIf=\"!(field.capabilities | pure: getWidgets)\"\n [ngSwitch]=\"meta.fieldType\"\n >\n <input\n *ngSwitchCase=\"SpecCapability.number\"\n type=\"number\"\n aui-input\n auiFormItemControl\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n />\n <ng-container *ngSwitchCase=\"SpecCapability.booleanSwitch\">\n <aui-switch\n auiFormItemControl\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"switchReadonly\"\n [formControl]=\"control\"\n ></aui-switch>\n <ng-template #switchReadonly>{{\n control.value\n | pure: convertBooleanSwitchValue : field.capabilities\n | translate\n }}</ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.select\">\n <aui-multi-select\n *ngIf=\"isMultiple\"\n [maxRowCount]=\"3\"\n auiFormItemControl\n [clearable]=\"true\"\n [filterable]=\"true\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [loading]=\"loading$ | async\"\n [allowCreate]=\"isAllowCreate\"\n >\n <aui-option\n *ngFor=\"let option of dynamicOptions$ | async\"\n [value]=\"option.value\"\n [label]=\"option.label\"\n >\n {{ option.label }}\n </aui-option>\n <aui-option-placeholder>{{\n 'no_data' | translate\n }}</aui-option-placeholder>\n </aui-multi-select>\n\n <aui-select\n *ngIf=\"!isMultiple\"\n auiFormItemControl\n [clearable]=\"true\"\n [filterable]=\"true\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [loading]=\"loading$ | async\"\n [allowCreate]=\"isAllowCreate\"\n >\n <aui-option\n *ngFor=\"let option of dynamicOptions$ | async\"\n [value]=\"option.value\"\n [label]=\"option.label | aclParseJsonTranslate\"\n >\n {{ option.label | aclParseJsonTranslate }}\n </aui-option>\n <aui-option-placeholder>{{\n 'no_data' | translate\n }}</aui-option-placeholder>\n </aui-select>\n\n <button\n *ngIf=\"(dynamicExpression$ | async).options\"\n style=\"margin-left: 4px\"\n aui-button\n type=\"button\"\n [square]=\"true\"\n [disabled]=\"\n (dynamicOptionRefreshDisabled$$ | async) ||\n (dynamicOptionLoading$$ | async)\n \"\n [loading]=\"dynamicOptionLoading$$ | async\"\n (click)=\"dynamicOptionRefresh$$.next()\"\n >\n <aui-icon icon=\"rotate_right\"></aui-icon>\n </button>\n </ng-container>\n <aui-radio-group\n *ngSwitchCase=\"SpecCapability.radio\"\n auiFormItemControl\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"radioLabel\"\n >\n <ng-container>\n <aui-radio-button\n *ngFor=\"let option of field.capabilities | pure: getRadioOptions\"\n [value]=\"option.value\"\n >\n {{ option.display | translate }}\n </aui-radio-button>\n <ng-template #radioLabel>\n {{\n control.value\n | pure\n : getOptionDisplay\n : (field.capabilities | pure: getRadioOptions)\n | translate\n }}\n </ng-template>\n </ng-container>\n </aui-radio-group>\n <ng-container *ngSwitchCase=\"SpecCapability.resourceRequirements\">\n <acl-crd-resource-requirements\n auiFormItemControl\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"resourceRequirementsReadonly\"\n ></acl-crd-resource-requirements>\n <ng-template #resourceRequirementsReadonly>\n <acl-crd-resource-requirements\n auiFormItemControl\n [ngModel]=\"control.value\"\n [readOnly]=\"true\"\n ></acl-crd-resource-requirements>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.yaml\">\n <acl-crd-yaml-editor\n auiFormItemControl\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [actionsConfig]=\"editorActions\"\n [options]=\"editorOptions\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"yamlReadonly\"\n ></acl-crd-yaml-editor>\n <ng-template #yamlReadonly>\n <acl-crd-yaml-editor\n auiFormItemControl\n [ngModel]=\"control.value\"\n [actionsConfig]=\"viewActions\"\n [options]=\"viewOptions\"\n ></acl-crd-yaml-editor>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.k8sResourcePrefix\">\n <acl-crd-resource-prefix\n auiFormItemControl\n [formErrors]=\"control.errors\"\n [field]=\"field\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [aclReadonlyFieldTemplate]=\"resourcePrefixReadonly\"\n ></acl-crd-resource-prefix>\n <ng-template #resourcePrefixReadonly>\n <acl-crd-resource-prefix\n auiFormItemControl\n [field]=\"field\"\n [ngModel]=\"control.value\"\n [readonly]=\"true\"\n ></acl-crd-resource-prefix>\n </ng-template>\n </ng-container>\n <ng-container *ngSwitchCase=\"SpecCapability.basicAuthSecret\">\n <acl-crd-basic-auth-secret\n auiFormItemControl\n [field]=\"field\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n ></acl-crd-basic-auth-secret>\n </ng-container>\n <textarea\n *ngSwitchCase=\"SpecCapability.textarea\"\n auiFormItemControl\n aui-input\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [aclReadonlyField]=\"isDisabled\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n ></textarea>\n <aui-tags-input\n *ngSwitchCase=\"SpecCapability.tagsInput\"\n auiFormItemControl\n class=\"tw-flex-1\"\n [required]=\"isRequired\"\n [formControl]=\"control\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n [aclReadonlyField]=\"isDisabled\"\n [clearable]=\"isClearable\"\n ></aui-tags-input>\n <ng-container *ngSwitchDefault>\n <acl-crd-link\n *ngIf=\"(field | pure: isLink) && isDisabled; else textField\"\n auiFormItemControl\n [field]=\"field\"\n [formControl]=\"control\"\n ></acl-crd-link>\n </ng-container>\n <ng-template #textField>\n <input\n type=\"text\"\n auiFormItemControl\n [required]=\"isRequired\"\n aui-input\n [formControl]=\"control\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n [aclReadonlyField]=\"isDisabled\"\n />\n </ng-template>\n </acl-crd-form-item>\n <div\n class=\"tw-flex tw-w-full tw-flex-col\"\n *ngIf=\"field.capabilities | pure: getWidgets as widget\"\n >\n <ng-container *ngComponentOutlet=\"widget.component\"></ng-container>\n </div>\n </ng-container>\n <ng-template #advancedFiled>\n <ng-container\n *ngIf=\"\n meta.fieldType === SpecCapability.password ||\n meta.fieldType === SpecCapability.confirmPassword ||\n meta.fieldType === SpecCapability.externalPassword\n \"\n >\n <acl-crd-form-item\n *ngIf=\"isDisabled\"\n [plain]=\"true\"\n >\n <ng-container *ngTemplateOutlet=\"passwordView\"></ng-container>\n </acl-crd-form-item>\n <acl-password-input\n *ngIf=\"!isDisabled\"\n [initPassword]=\"control.value\"\n [label]=\"field | pure: getFieldDisplayName | translate\"\n [hint]=\"field | pure: getFieldDescription | translate\"\n [formControl]=\"control\"\n [required]=\"isRequired\"\n [readonly]=\"crdForm.readonly\"\n [isUpdate]=\"!!control.value && !control.dirty\"\n [strongPassword]=\"meta.fieldType !== SpecCapability.externalPassword\"\n [enableConfirm]=\"meta.fieldType === SpecCapability.confirmPassword\"\n width=\"large\"\n [encoding]=\"field | pure: getEncoding : 'password'\"\n [pattern]=\"field.validations?.pattern\"\n [placeholder]=\"field | pure: getFieldPlaceholder | translate\"\n ></acl-password-input>\n </ng-container>\n <!-- group field -->\n <ng-container *ngIf=\"isGroupField(field) && field.fieldList?.length > 0\">\n <acl-operand-field-group\n [fields]=\"fields\"\n [fieldGroup]=\"field\"\n [readonly]=\"isDisabled\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-operand-field-group>\n </ng-container>\n <!-- array field groups -->\n <!-- array field table -->\n <ng-container *ngIf=\"isArrayField(field)\">\n <acl-crd-array-table\n *ngIf=\"field | pure: isArrayFieldTable; else defaultArrayFiled\"\n [arrayFieldGroup]=\"field\"\n [formDataState]=\"formDataState\"\n [readonly]=\"isDisabled\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-crd-array-table>\n <ng-template #defaultArrayFiled>\n <acl-operand-array-field-group\n [arrayFieldGroup]=\"field\"\n [fields]=\"fields\"\n [readonly]=\"isDisabled\"\n [formDataState]=\"formDataState\"\n (valueChange)=\"valueChange.emit($event)\"\n >\n </acl-operand-array-field-group>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-container>\n</ng-container>\n\n<ng-template #passwordView>\n {{ PASSWORD_REVEAL }}\n</ng-template>\n", styles: [":host .input-group{width:100%}:host ::ng-deep .aui-form-item__label-wrapper:not(.hasLabel){display:none}\n"] }]
|
|
2179
|
+
}], propDecorators: { field: [{
|
|
2180
|
+
type: Input
|
|
2181
|
+
}], field$: [], readonly: [{
|
|
2182
|
+
type: Input
|
|
2183
|
+
}], fields: [{
|
|
2184
|
+
type: Input
|
|
2185
|
+
}], formDataState: [{
|
|
2186
|
+
type: Input
|
|
2187
|
+
}], formDataState$: [], formErrors: [{
|
|
2188
|
+
type: Input
|
|
2189
|
+
}], valueChange: [{
|
|
2190
|
+
type: Output
|
|
2191
|
+
}], itemRemove: [{
|
|
2192
|
+
type: Output
|
|
2193
|
+
}], isHidden: [{
|
|
2194
|
+
type: HostBinding,
|
|
2195
|
+
args: ['hidden']
|
|
2196
|
+
}] } });
|
|
2197
|
+
|
|
2198
|
+
class OperandAdvancedFieldGroupComponent {
|
|
2199
|
+
constructor() {
|
|
2200
|
+
this.readonly = false;
|
|
2201
|
+
this.valueChange = new EventEmitter();
|
|
2202
|
+
this.expanded = false;
|
|
2203
|
+
this.startCase = startCase;
|
|
2204
|
+
}
|
|
2205
|
+
fieldValueChange(e) {
|
|
2206
|
+
this.valueChange.emit(e);
|
|
2207
|
+
}
|
|
2208
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandAdvancedFieldGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2209
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: OperandAdvancedFieldGroupComponent, isStandalone: true, selector: "acl-operand-advanced-field-group", inputs: { fields: "fields", fieldList: "fieldList", formDataState: "formDataState", formErrors: "formErrors", readonly: "readonly" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div\n [class.expanded]=\"expanded\"\n class=\"field-group\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n @if (expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n <span>{{ 'advanced_fields' | translate }}</span>\n </div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (field of fieldList; track field) {\n <acl-operand-field\n [field]=\"field\"\n [fields]=\"fields\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => IconModule) }, { kind: "component", type: i0.forwardRef(() => i2.IconComponent), selector: "aui-icon", inputs: ["icon", "light", "dark", "link", "margin", "size", "color", "background", "backgroundColor"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldComponent), selector: "acl-operand-field", inputs: ["field", "readonly", "fields", "formDataState", "formErrors"], outputs: ["valueChange", "itemRemove"] }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2210
|
+
}
|
|
2211
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: OperandAdvancedFieldGroupComponent, decorators: [{
|
|
2212
|
+
type: Component,
|
|
2213
|
+
args: [{ selector: 'acl-operand-advanced-field-group', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [TranslatePipe, IconModule, forwardRef(() => OperandFieldComponent)], template: "<div\n [class.expanded]=\"expanded\"\n class=\"field-group\"\n>\n <div class=\"header\">\n <div\n class=\"title\"\n (click)=\"expanded = !expanded\"\n >\n @if (!expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n @if (expanded) {\n <aui-icon\n size=\"20px,20px\"\n icon=\"caret_down_s\"\n ></aui-icon>\n }\n <span>{{ 'advanced_fields' | translate }}</span>\n </div>\n </div>\n <div\n class=\"content\"\n [hidden]=\"!expanded\"\n >\n @for (field of fieldList; track field) {\n <acl-operand-field\n [field]=\"field\"\n [fields]=\"fields\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n </div>\n</div>\n", styles: [".field-group{border:1px solid rgb(var(--aui-color-n-7));background-color:rgb(var(--aui-color-n-10));position:relative;margin-bottom:16px;border-radius:2px}.field-group .title{padding:0 20px;font-size:14px;font-weight:500;display:flex;align-items:center;height:60px;line-height:20px;color:rgb(var(--aui-color-n-1));cursor:pointer}.field-group .title aui-icon{margin-right:20px;font-size:20px}.field-group .description{display:block;padding-left:22px;font-size:12px;line-height:16px;color:rgb(var(--aui-color-n-4))}.field-group:not(.expanded) aui-icon[icon=caret_down_s]{transform:rotate(-90deg)}.field-group.expanded>.header>.title{border-bottom:1px solid rgb(var(--aui-color-n-8))}.field-group.expanded>.header>.description{display:block;margin-top:4px}.field-group .content{padding:8px 12px}.field-group .content.no-title{padding-top:0}.field-group .array-item__remove{display:flex;justify-content:flex-end;margin-bottom:4px}.field-group .array-item__add,.field-group .array-item__remove{text-align:center}.field-group .array-item__add aui-icon,.field-group .array-item__remove aui-icon{margin-right:4px}\n"] }]
|
|
2214
|
+
}], propDecorators: { fields: [{
|
|
2215
|
+
type: Input
|
|
2216
|
+
}], fieldList: [{
|
|
2217
|
+
type: Input
|
|
2218
|
+
}], formDataState: [{
|
|
2219
|
+
type: Input
|
|
2220
|
+
}], formErrors: [{
|
|
2221
|
+
type: Input
|
|
2222
|
+
}], readonly: [{
|
|
2223
|
+
type: Input
|
|
2224
|
+
}], valueChange: [{
|
|
2225
|
+
type: Output
|
|
2226
|
+
}] } });
|
|
2227
|
+
|
|
2228
|
+
// 去除 others 和 paths 中主 path 相同的部分
|
|
2229
|
+
function differPathsTag(paths, others) {
|
|
2230
|
+
return differenceBy(paths, others, pathWithTag => getPathFromTagPath(pathWithTag));
|
|
2231
|
+
}
|
|
2232
|
+
function getAllOtherTagPath(fields, paths) {
|
|
2233
|
+
return fields
|
|
2234
|
+
.map(path => getOperandPath(path))
|
|
2235
|
+
.filter(path => !paths.includes(path));
|
|
2236
|
+
}
|
|
2237
|
+
function matchValueAgainstOneOf(field, currentValue) {
|
|
2238
|
+
// oneOfMap 里应当为 tagged path
|
|
2239
|
+
const oneOfMap = getOneOfMap(field);
|
|
2240
|
+
// 使用两个 set 处理多条 OneOf 中存在公共 path 的情形
|
|
2241
|
+
const dirtyPathSet = new Set();
|
|
2242
|
+
const validPathSet = new Set();
|
|
2243
|
+
Object.entries(oneOfMap).forEach(([value, paths]) => {
|
|
2244
|
+
paths.forEach(path => {
|
|
2245
|
+
(convertValue(value, field.type) === currentValue
|
|
2246
|
+
? validPathSet
|
|
2247
|
+
: dirtyPathSet).add(path);
|
|
2248
|
+
});
|
|
2249
|
+
});
|
|
2250
|
+
return {
|
|
2251
|
+
dirtyPaths: Array.from(dirtyPathSet)
|
|
2252
|
+
.filter(i => !validPathSet.has(i))
|
|
2253
|
+
.map(path => normalizePath(path)),
|
|
2254
|
+
validPaths: Array.from(validPathSet).map(path => normalizePath(path)),
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
class CrdFormComponent {
|
|
2259
|
+
constructor() {
|
|
2260
|
+
this.debug = false;
|
|
2261
|
+
this.openApiSchemaPath = SCHEMA_PATH;
|
|
2262
|
+
this.readonly = false;
|
|
2263
|
+
this.labelPosition = LabelPosition.Right;
|
|
2264
|
+
this.formStateChange = new EventEmitter();
|
|
2265
|
+
this.formSchemaChange = new EventEmitter();
|
|
2266
|
+
this.formErrors = {};
|
|
2267
|
+
// 一个全局的 controlMap,方便控制 field 主动校验。
|
|
2268
|
+
// 该 controlMap 以及所有 field 里的 control 实例,都是为了数据追踪与错误同步(control.error -> crdForm error),本身不影响提交,由 crd-form 的 error 去阻塞提交。
|
|
2269
|
+
this.controlMap = new Map();
|
|
2270
|
+
// 被隐藏起来的表单的路径集合
|
|
2271
|
+
this.dirtyPathsSet = new Set();
|
|
2272
|
+
}
|
|
2273
|
+
ngOnChanges({ openApiSchema, descriptors, data }) {
|
|
2274
|
+
if (openApiSchema?.currentValue || descriptors?.currentValue) {
|
|
2275
|
+
this.setUpForm();
|
|
2276
|
+
}
|
|
2277
|
+
else if (data?.currentValue) {
|
|
2278
|
+
this.formDataState = fromJS(data.currentValue);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
setUpForm() {
|
|
2282
|
+
this.formDataState = fromJS(this.data || {});
|
|
2283
|
+
const { normalFields, fieldGroups, arrayFieldGroups, advancedFields, descriptorFields, openApiFields, basicFields, treeFields, } = createCapabilityField(this.descriptors || [], this.openApiSchema || {}, this.data, this.openApiSchemaPath);
|
|
2284
|
+
this.fields = basicFields.filter(fieldShouldReveal);
|
|
2285
|
+
if (this.debug) {
|
|
2286
|
+
/* eslint-disable no-console */
|
|
2287
|
+
console.info('openApiFields:', openApiFields);
|
|
2288
|
+
console.info('descriptorFields:', descriptorFields);
|
|
2289
|
+
console.info('basicFields:', basicFields);
|
|
2290
|
+
console.info('normalFields', normalFields);
|
|
2291
|
+
console.info('fieldGroups:', fieldGroups);
|
|
2292
|
+
console.info('arrayFieldGroups:', arrayFieldGroups);
|
|
2293
|
+
console.info('advancedFields:', advancedFields);
|
|
2294
|
+
console.info('treeFields:', treeFields);
|
|
2295
|
+
/* eslint-enable no-console */
|
|
2296
|
+
}
|
|
2297
|
+
this.normalFields = normalFields;
|
|
2298
|
+
this.fieldGroups = fieldGroups;
|
|
2299
|
+
this.arrayFieldGroups = arrayFieldGroups;
|
|
2300
|
+
this.advancedFields = advancedFields;
|
|
2301
|
+
this.formSchemaChange.emit({
|
|
2302
|
+
openApiFields,
|
|
2303
|
+
descriptorFields,
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2306
|
+
fieldValueChange(event) {
|
|
2307
|
+
let onceDirtyPathsSet = new Set();
|
|
2308
|
+
// TODO: 待review确认后移除该开关
|
|
2309
|
+
if (this.formContext?.gates?.includes('onceDirtyPathsSet')) {
|
|
2310
|
+
const operandPath = getOperandPath(event.field);
|
|
2311
|
+
// 如果表单被隐藏且这次change是因为直接改变了包含该路径的父路径的值(例如编辑yaml)
|
|
2312
|
+
// 那就阻止此子路径值的change逻辑
|
|
2313
|
+
if (this.dirtyPathsSet.has(operandPath) &&
|
|
2314
|
+
this._lastUpdateField?.path &&
|
|
2315
|
+
operandPath.startsWith(`${this._lastUpdateField.path}.`)) {
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
else {
|
|
2320
|
+
onceDirtyPathsSet = this.dirtyPathsSet;
|
|
2321
|
+
}
|
|
2322
|
+
const eventData = event.data;
|
|
2323
|
+
if (event.field.capabilities.some(c => c.startsWith(SpecCapability.oneOf))) {
|
|
2324
|
+
const { dirtyPaths, validPaths } = this.resolveExpiredPath(event.field, eventData);
|
|
2325
|
+
dirtyPaths.forEach(path => {
|
|
2326
|
+
this.dirtyPathsSet.add(normalizePath(path));
|
|
2327
|
+
onceDirtyPathsSet.add(normalizePath(path));
|
|
2328
|
+
});
|
|
2329
|
+
validPaths.forEach(path => {
|
|
2330
|
+
this.updateValidPathValue(path);
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
const allOtherPaths = getAllOtherTagPath(this.fields, Array.from(onceDirtyPathsSet));
|
|
2334
|
+
const removedPaths = uniq(
|
|
2335
|
+
// 同一 path 可能有多个 tag,假设某个 tagged path 有效,则其对应 path 数据不应该删除
|
|
2336
|
+
differPathsTag(Array.from(onceDirtyPathsSet), allOtherPaths).map(path => getPathFromTagPath(path))).filter(
|
|
2337
|
+
// 只清空叶子结点,以适配 yaml 情况
|
|
2338
|
+
path => !this.fields.some(field => {
|
|
2339
|
+
const isParentPath = field.path.startsWith(`${path}.`);
|
|
2340
|
+
if (isParentPath) {
|
|
2341
|
+
// 该 field 亦为 dirty,不以为依据
|
|
2342
|
+
return !onceDirtyPathsSet.has(field.path);
|
|
2343
|
+
}
|
|
2344
|
+
return false;
|
|
2345
|
+
}));
|
|
2346
|
+
if (eventData == null && !this.keepEmptyPath) {
|
|
2347
|
+
if (!onceDirtyPathsSet.has(getOperandPath(event.field))) {
|
|
2348
|
+
this.deleteFormData(event.field.path);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
else {
|
|
2352
|
+
this.updateFormData(event.field, eventData);
|
|
2353
|
+
}
|
|
2354
|
+
removedPaths.forEach(path => {
|
|
2355
|
+
this.deleteFormData(path);
|
|
2356
|
+
});
|
|
2357
|
+
this.refreshValidation();
|
|
2358
|
+
this.formStateChange.emit({
|
|
2359
|
+
data: this.formDataState.toJS(),
|
|
2360
|
+
errors: this.formErrors,
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
deleteFormData(path) {
|
|
2364
|
+
this.formDataState = this.formDataState.deleteIn(pathToArray(path));
|
|
2365
|
+
this.controlMap.get(path)?.setValue(null);
|
|
2366
|
+
this.formStateChange.emit({
|
|
2367
|
+
data: this.formDataState.toJS(),
|
|
2368
|
+
errors: this.formErrors,
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
updateValidPathValue(path) {
|
|
2372
|
+
// 如果有效路径是上一次的无效路径,则意味着该路径的值在上次被重置了,所以当该路径又需要显示给用户时应该使用默认值
|
|
2373
|
+
const useDefault = this.dirtyPathsSet.has(normalizePath(path));
|
|
2374
|
+
this.dirtyPathsSet.delete(normalizePath(path));
|
|
2375
|
+
// 如果对一组表单都有依赖,那路径可能是这组表单的父路径,所以需要把所有子孙都找出来
|
|
2376
|
+
const fields = getFieldsByPath(path, [
|
|
2377
|
+
...this.normalFields,
|
|
2378
|
+
...flattenDeep(this.fieldGroups.map(group => group.fieldList)),
|
|
2379
|
+
...flattenDeep(this.arrayFieldGroups.map(arrayGroup => arrayGroup.fieldLists)),
|
|
2380
|
+
...this.advancedFields,
|
|
2381
|
+
]);
|
|
2382
|
+
fields.forEach(field => {
|
|
2383
|
+
// 理论上 oneof 只影响视图,但实际还会影响 formDataState,所以需要同步更新 formDataState
|
|
2384
|
+
let value = this.controlMap.get(getOperandPath(field)).value;
|
|
2385
|
+
if (!value && useDefault) {
|
|
2386
|
+
value = getDefaultValue(field);
|
|
2387
|
+
}
|
|
2388
|
+
this.updateFormData(field, value);
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
updateFormData(field, data) {
|
|
2392
|
+
if (!field)
|
|
2393
|
+
return;
|
|
2394
|
+
const { match, index, pathBeforeIndex, pathAfterIndex } = parseArrayPath(field.path);
|
|
2395
|
+
if (match && index === 0) {
|
|
2396
|
+
this.formDataState = this.initializeFormDataArrayProperty(this.formDataState, pathBeforeIndex, pathAfterIndex, data);
|
|
2397
|
+
}
|
|
2398
|
+
this.formDataState = this.formDataState.setIn(pathToArray(field.path), data);
|
|
2399
|
+
this._lastUpdateField = field;
|
|
2400
|
+
}
|
|
2401
|
+
refreshValidation() {
|
|
2402
|
+
const errors = Array.from(this.controlMap).reduce((acc, [pathWithTag, control]) => {
|
|
2403
|
+
if (this.dirtyPathsSet.has(pathWithTag)) {
|
|
2404
|
+
return acc;
|
|
2405
|
+
}
|
|
2406
|
+
const path = getPathFromTagPath(pathWithTag);
|
|
2407
|
+
acc[path] = isEmpty(control.errors) ? null : control.errors;
|
|
2408
|
+
return acc;
|
|
2409
|
+
}, {});
|
|
2410
|
+
const result = fromPairs(Object.entries(errors ?? {}).filter(([_k, v]) => !!v));
|
|
2411
|
+
this.formErrors = isEmpty(result) ? null : result;
|
|
2412
|
+
}
|
|
2413
|
+
// 当某个 field 发生数据变化,返回其相关 oneOf path 是否应该移除
|
|
2414
|
+
// 仅当当前 field 中 oneOf 条件与当前数据不匹配,
|
|
2415
|
+
resolveExpiredPath(field, currentValue) {
|
|
2416
|
+
const { dirtyPaths, validPaths } = matchValueAgainstOneOf(field, currentValue);
|
|
2417
|
+
const otherFieldStatus = this.fields
|
|
2418
|
+
.filter(i => getOperandPath(i) !== getOperandPath(field))
|
|
2419
|
+
.map(field => ({
|
|
2420
|
+
path: getOperandPath(field),
|
|
2421
|
+
result: matchValueAgainstOneOf(field, this.controlMap.get(getOperandPath(field))?.value),
|
|
2422
|
+
}));
|
|
2423
|
+
const newDirtyPaths = validPaths.filter(path => {
|
|
2424
|
+
// 当前 valid path 在其他 field 中为 dirty,若该 field 本身也为 dirty 则忽略该影响,仍然判定为 valid,否则(该 field 有效)认定为 dirty
|
|
2425
|
+
const fieldCandidates = otherFieldStatus.filter(status => status.result.dirtyPaths.includes(path));
|
|
2426
|
+
return fieldCandidates.some(({ path }) => !otherFieldStatus.some(status => status.result.dirtyPaths.includes(path)));
|
|
2427
|
+
});
|
|
2428
|
+
return {
|
|
2429
|
+
dirtyPaths: [...dirtyPaths, ...newDirtyPaths],
|
|
2430
|
+
validPaths: validPaths.filter(path => !newDirtyPaths.includes(path)),
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
initializeFormDataArrayProperty(state, pathBeforeIndex, pathAfterIndex, value) {
|
|
2434
|
+
if (state.getIn([...pathToArray(pathBeforeIndex), 0])) {
|
|
2435
|
+
return state;
|
|
2436
|
+
}
|
|
2437
|
+
const item = Map$1({}).setIn(pathToArray(pathAfterIndex), value);
|
|
2438
|
+
const list = List([item]);
|
|
2439
|
+
return state.setIn(pathToArray(pathBeforeIndex), list);
|
|
2440
|
+
}
|
|
2441
|
+
submit() {
|
|
2442
|
+
this.refreshValidation();
|
|
2443
|
+
this.form.onSubmit(null);
|
|
2444
|
+
}
|
|
2445
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CrdFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2446
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: CrdFormComponent, isStandalone: true, selector: "acl-crd-form", inputs: { openApiSchema: "openApiSchema", descriptors: "descriptors", data: "data", debug: "debug", openApiSchemaPath: "openApiSchemaPath", readonly: "readonly", labelWidth: "labelWidth", labelPosition: "labelPosition", cluster: "cluster", namespace: "namespace", formContext: "formContext", keepEmptyPath: "keepEmptyPath", effectControlDefaultValue: "effectControlDefaultValue", uiContext: "uiContext", widgets: "widgets" }, outputs: { formStateChange: "formStateChange", formSchemaChange: "formSchemaChange" }, viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<form\n auiForm\n [aclScrollToFirstInvalid]=\"!!formErrors\"\n #form=\"ngForm\"\n [auiFormLabelPosition]=\"labelPosition\"\n [auiFormLabelWidth]=\"labelWidth\"\n>\n <!-- normal fields -->\n @for (field of normalFields; track field) {\n <acl-operand-field\n [fields]=\"fields\"\n [field]=\"field\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n\n <!-- field groups \u517C\u5BB9\uFF0C\u9700\u8981\u4FDD\u7559-->\n @for (group of fieldGroups; track group) {\n <acl-operand-field-group\n [fields]=\"fields\"\n [fieldGroup]=\"group\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field-group>\n }\n\n <!-- array field groups \u517C\u5BB9\uFF0C\u9700\u8981\u4FDD\u7559 -->\n @for (group of arrayFieldGroups; track group) {\n <acl-operand-array-field-group\n [arrayFieldGroup]=\"group\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n (itemRemove)=\"deleteFormData($event)\"\n >\n </acl-operand-array-field-group>\n }\n\n <!-- advanced fields -->\n @if (advancedFields?.length) {\n <acl-operand-advanced-field-group\n [fields]=\"fields\"\n [fieldList]=\"advancedFields\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n ></acl-operand-advanced-field-group>\n }\n</form>\n", styles: [":host{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => FormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1.ɵNgNoValidate), selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i0.forwardRef(() => i1.NgControlStatusGroup), selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i0.forwardRef(() => i1.NgForm), selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: i0.forwardRef(() => FormModule) }, { kind: "directive", type: i0.forwardRef(() => i2.FormDirective), selector: "form[auiForm]", inputs: ["auiFormLabelWidth", "auiFormLabelPosition", "auiFormEmptyAddon", "auiFormInline"], exportAs: ["auiForm"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldComponent), selector: "acl-operand-field", inputs: ["field", "readonly", "fields", "formDataState", "formErrors"], outputs: ["valueChange", "itemRemove"] }, { kind: "component", type: i0.forwardRef(() => OperandFieldGroupComponent), selector: "acl-operand-field-group", inputs: ["fields", "fieldGroup", "formDataState", "formErrors", "readonly"], outputs: ["valueChange"] }, { kind: "component", type: i0.forwardRef(() => OperandArrayFieldGroupComponent), selector: "acl-operand-array-field-group" }, { kind: "component", type: i0.forwardRef(() => OperandAdvancedFieldGroupComponent), selector: "acl-operand-advanced-field-group", inputs: ["fields", "fieldList", "formDataState", "formErrors", "readonly"], outputs: ["valueChange"] }, { kind: "directive", type: i0.forwardRef(() => ScrollToFirstInvalidDirective), selector: "[aclScrollToFirstInvalid]", inputs: ["labelOffset", "aclScrollToFirstInvalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2447
|
+
}
|
|
2448
|
+
__decorate([
|
|
2449
|
+
ObservableInput(),
|
|
2450
|
+
__metadata("design:type", Observable)
|
|
2451
|
+
], CrdFormComponent.prototype, "formContext$", void 0);
|
|
2452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CrdFormComponent, decorators: [{
|
|
2453
|
+
type: Component,
|
|
2454
|
+
args: [{ selector: 'acl-crd-form', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
2455
|
+
FormsModule,
|
|
2456
|
+
FormModule,
|
|
2457
|
+
forwardRef(() => OperandFieldComponent),
|
|
2458
|
+
forwardRef(() => OperandFieldGroupComponent),
|
|
2459
|
+
forwardRef(() => OperandArrayFieldGroupComponent),
|
|
2460
|
+
forwardRef(() => OperandAdvancedFieldGroupComponent),
|
|
2461
|
+
ScrollToFirstInvalidDirective,
|
|
2462
|
+
], template: "<form\n auiForm\n [aclScrollToFirstInvalid]=\"!!formErrors\"\n #form=\"ngForm\"\n [auiFormLabelPosition]=\"labelPosition\"\n [auiFormLabelWidth]=\"labelWidth\"\n>\n <!-- normal fields -->\n @for (field of normalFields; track field) {\n <acl-operand-field\n [fields]=\"fields\"\n [field]=\"field\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field>\n }\n\n <!-- field groups \u517C\u5BB9\uFF0C\u9700\u8981\u4FDD\u7559-->\n @for (group of fieldGroups; track group) {\n <acl-operand-field-group\n [fields]=\"fields\"\n [fieldGroup]=\"group\"\n [formDataState]=\"formDataState\"\n [formErrors]=\"formErrors\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n >\n </acl-operand-field-group>\n }\n\n <!-- array field groups \u517C\u5BB9\uFF0C\u9700\u8981\u4FDD\u7559 -->\n @for (group of arrayFieldGroups; track group) {\n <acl-operand-array-field-group\n [arrayFieldGroup]=\"group\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n (itemRemove)=\"deleteFormData($event)\"\n >\n </acl-operand-array-field-group>\n }\n\n <!-- advanced fields -->\n @if (advancedFields?.length) {\n <acl-operand-advanced-field-group\n [fields]=\"fields\"\n [fieldList]=\"advancedFields\"\n [formDataState]=\"formDataState\"\n [readonly]=\"readonly\"\n (valueChange)=\"fieldValueChange($event)\"\n ></acl-operand-advanced-field-group>\n }\n</form>\n", styles: [":host{width:100%}\n"] }]
|
|
2463
|
+
}], propDecorators: { openApiSchema: [{
|
|
2464
|
+
type: Input
|
|
2465
|
+
}], descriptors: [{
|
|
2466
|
+
type: Input
|
|
2467
|
+
}], data: [{
|
|
2468
|
+
type: Input
|
|
2469
|
+
}], debug: [{
|
|
2470
|
+
type: Input
|
|
2471
|
+
}], openApiSchemaPath: [{
|
|
2472
|
+
type: Input
|
|
2473
|
+
}], readonly: [{
|
|
2474
|
+
type: Input
|
|
2475
|
+
}], labelWidth: [{
|
|
2476
|
+
type: Input
|
|
2477
|
+
}], labelPosition: [{
|
|
2478
|
+
type: Input
|
|
2479
|
+
}], cluster: [{
|
|
2480
|
+
type: Input
|
|
2481
|
+
}], namespace: [{
|
|
2482
|
+
type: Input
|
|
2483
|
+
}], formContext: [{
|
|
2484
|
+
type: Input
|
|
2485
|
+
}], formContext$: [], keepEmptyPath: [{
|
|
2486
|
+
type: Input
|
|
2487
|
+
}], effectControlDefaultValue: [{
|
|
2488
|
+
type: Input
|
|
2489
|
+
}], uiContext: [{
|
|
2490
|
+
type: Input
|
|
2491
|
+
}], formStateChange: [{
|
|
2492
|
+
type: Output
|
|
2493
|
+
}], formSchemaChange: [{
|
|
2494
|
+
type: Output
|
|
2495
|
+
}], form: [{
|
|
2496
|
+
type: ViewChild,
|
|
2497
|
+
args: ['form', { static: true }]
|
|
2498
|
+
}], widgets: [{
|
|
2499
|
+
type: Input
|
|
2500
|
+
}] } });
|
|
2501
|
+
|
|
2502
|
+
class BaseOperandFiledArrayComponent {
|
|
2503
|
+
constructor() {
|
|
2504
|
+
this.readonly = false;
|
|
2505
|
+
this.valueChange = new EventEmitter();
|
|
2506
|
+
this.itemRemove = new EventEmitter();
|
|
2507
|
+
this.getFieldDisplayName = getFieldDisplayName;
|
|
2508
|
+
this.crdForm = inject(CrdFormComponent);
|
|
2509
|
+
}
|
|
2510
|
+
get expanded() {
|
|
2511
|
+
return this._expanded ?? !isFolded(this.arrayFieldGroup);
|
|
2512
|
+
}
|
|
2513
|
+
set expanded(expanded) {
|
|
2514
|
+
this._expanded = expanded;
|
|
2515
|
+
}
|
|
2516
|
+
ngOnChanges({ arrayFieldGroup, formDataState }) {
|
|
2517
|
+
if (!this.basicArrayFieldTemplate &&
|
|
2518
|
+
arrayFieldGroup.currentValue?.fieldLists?.length > 0) {
|
|
2519
|
+
this.basicArrayFieldTemplate = last(arrayFieldGroup.currentValue.fieldLists);
|
|
2520
|
+
}
|
|
2521
|
+
if (formDataState?.currentValue && formDataState?.previousValue) {
|
|
2522
|
+
const currValue = getFormData(formDataState.currentValue, this.arrayFieldGroup.path);
|
|
2523
|
+
const arrayFieldLength = this.arrayFieldGroup.fieldLists?.length;
|
|
2524
|
+
if (currValue?.size > arrayFieldLength) {
|
|
2525
|
+
Array.from({ length: currValue.size - arrayFieldLength }).forEach(() => {
|
|
2526
|
+
this.addItem();
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
fieldValueChange(e) {
|
|
2532
|
+
this.valueChange.emit(e);
|
|
2533
|
+
}
|
|
2534
|
+
addItem() {
|
|
2535
|
+
const newFields = this.basicArrayFieldTemplate.map(field => {
|
|
2536
|
+
const toPath = modifyArrayFieldPathIndex(field.path, () => this.arrayFieldGroup.fieldLists.length);
|
|
2537
|
+
return {
|
|
2538
|
+
...repairFieldChildPathByCorrectIndex(field, field.path, toPath),
|
|
2539
|
+
};
|
|
2540
|
+
});
|
|
2541
|
+
this.arrayFieldGroup.fieldLists = [
|
|
2542
|
+
...this.arrayFieldGroup.fieldLists,
|
|
2543
|
+
newFields,
|
|
2544
|
+
];
|
|
2545
|
+
}
|
|
2546
|
+
removeItem(index) {
|
|
2547
|
+
const groupToRemove = this.arrayFieldGroup.fieldLists[index];
|
|
2548
|
+
const groupUntouched = this.arrayFieldGroup.fieldLists.filter((_, idx) => idx < index);
|
|
2549
|
+
const groupToLeftShift = this.arrayFieldGroup.fieldLists.filter((_, idx) => idx > index);
|
|
2550
|
+
const leftShiftedGroups = groupToLeftShift.map(group => group.map(field => {
|
|
2551
|
+
const toPath = modifyArrayFieldPathIndex(field.path, idx => idx - 1);
|
|
2552
|
+
return {
|
|
2553
|
+
...repairFieldChildPathByCorrectIndex(field, field.path, toPath),
|
|
2554
|
+
};
|
|
2555
|
+
}));
|
|
2556
|
+
this.arrayFieldGroup.fieldLists = [...groupUntouched, ...leftShiftedGroups];
|
|
2557
|
+
const [match, formDataPathToRemove] = /^(.*\[\d+]).*$/.exec(groupToRemove?.[0].path) || [];
|
|
2558
|
+
if (match) {
|
|
2559
|
+
this.itemRemove.next(formDataPathToRemove);
|
|
2560
|
+
}
|
|
2561
|
+
this.crdForm.controlMap.forEach((_, key) => {
|
|
2562
|
+
if (key.startsWith(`${this.arrayFieldGroup.path}[${this.arrayFieldGroup.fieldLists.length}]`)) {
|
|
2563
|
+
this.crdForm.controlMap.delete(key);
|
|
2564
|
+
}
|
|
2565
|
+
});
|
|
2566
|
+
this.crdForm.refreshValidation();
|
|
2567
|
+
this.deleteFormData(`${this.arrayFieldGroup.path}[${index}]`);
|
|
2568
|
+
}
|
|
2569
|
+
deleteFormData(path) {
|
|
2570
|
+
this.valueChange.next({
|
|
2571
|
+
field: {
|
|
2572
|
+
...this.arrayFieldGroup,
|
|
2573
|
+
path,
|
|
2574
|
+
},
|
|
2575
|
+
data: null,
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
fieldIsRequired(field) {
|
|
2579
|
+
return (field?.required ||
|
|
2580
|
+
Object.keys(field?.validations || {})?.includes(Validations.required));
|
|
2581
|
+
}
|
|
2582
|
+
get columnFields() {
|
|
2583
|
+
return this.basicArrayFieldTemplate;
|
|
2584
|
+
}
|
|
2585
|
+
trackByFn(index) {
|
|
2586
|
+
return index;
|
|
2587
|
+
}
|
|
2588
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseOperandFiledArrayComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2589
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: BaseOperandFiledArrayComponent, isStandalone: true, inputs: { arrayFieldGroup: "arrayFieldGroup", formDataState: "formDataState", formErrors: "formErrors", isArrayTable: "isArrayTable", readonly: "readonly", fields: "fields" }, outputs: { valueChange: "valueChange", itemRemove: "itemRemove" }, usesOnChanges: true, ngImport: i0 }); }
|
|
2590
|
+
}
|
|
2591
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseOperandFiledArrayComponent, decorators: [{
|
|
2592
|
+
type: Directive
|
|
2593
|
+
}], propDecorators: { arrayFieldGroup: [{
|
|
2594
|
+
type: Input
|
|
2595
|
+
}], formDataState: [{
|
|
2596
|
+
type: Input
|
|
2597
|
+
}], formErrors: [{
|
|
2598
|
+
type: Input
|
|
2599
|
+
}], isArrayTable: [{
|
|
2600
|
+
type: Input
|
|
2601
|
+
}], readonly: [{
|
|
2602
|
+
type: Input
|
|
2603
|
+
}], fields: [{
|
|
2604
|
+
type: Input
|
|
2605
|
+
}], valueChange: [{
|
|
2606
|
+
type: Output
|
|
2607
|
+
}], itemRemove: [{
|
|
2608
|
+
type: Output
|
|
2609
|
+
}] } });
|
|
2610
|
+
|
|
2611
|
+
const WIDGETS = [
|
|
2612
|
+
CrdFormArrayTableComponent,
|
|
2613
|
+
K8sResourcePrefixComponent,
|
|
2614
|
+
ResourceRequirementsComponent,
|
|
2615
|
+
YamlEditorComponent,
|
|
2616
|
+
BasicAuthSecretComponent,
|
|
2617
|
+
BasicAuthCreateSecretDialogComponent,
|
|
2618
|
+
];
|
|
2619
|
+
const EXPORTABLES = [
|
|
2620
|
+
FormItemComponent,
|
|
2621
|
+
OperandAdvancedFieldGroupComponent,
|
|
2622
|
+
OperandArrayFieldGroupComponent,
|
|
2623
|
+
OperandFieldGroupComponent,
|
|
2624
|
+
OperandFieldComponent,
|
|
2625
|
+
...WIDGETS,
|
|
2626
|
+
];
|
|
2627
|
+
class FieldControlsModule {
|
|
2628
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: FieldControlsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
2629
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.1", ngImport: i0, type: FieldControlsModule, imports: [FormItemComponent,
|
|
2630
|
+
OperandAdvancedFieldGroupComponent,
|
|
2631
|
+
OperandArrayFieldGroupComponent,
|
|
2632
|
+
OperandFieldGroupComponent,
|
|
2633
|
+
OperandFieldComponent, CrdFormArrayTableComponent,
|
|
2634
|
+
K8sResourcePrefixComponent,
|
|
2635
|
+
ResourceRequirementsComponent,
|
|
2636
|
+
YamlEditorComponent,
|
|
2637
|
+
BasicAuthSecretComponent,
|
|
2638
|
+
BasicAuthCreateSecretDialogComponent], exports: [FormItemComponent,
|
|
2639
|
+
OperandAdvancedFieldGroupComponent,
|
|
2640
|
+
OperandArrayFieldGroupComponent,
|
|
2641
|
+
OperandFieldGroupComponent,
|
|
2642
|
+
OperandFieldComponent, CrdFormArrayTableComponent,
|
|
2643
|
+
K8sResourcePrefixComponent,
|
|
2644
|
+
ResourceRequirementsComponent,
|
|
2645
|
+
YamlEditorComponent,
|
|
2646
|
+
BasicAuthSecretComponent,
|
|
2647
|
+
BasicAuthCreateSecretDialogComponent] }); }
|
|
2648
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: FieldControlsModule, imports: [EXPORTABLES] }); }
|
|
2649
|
+
}
|
|
2650
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: FieldControlsModule, decorators: [{
|
|
2651
|
+
type: NgModule,
|
|
2652
|
+
args: [{
|
|
2653
|
+
imports: [...EXPORTABLES],
|
|
2654
|
+
exports: EXPORTABLES,
|
|
2655
|
+
}]
|
|
2656
|
+
}] });
|
|
2657
|
+
|
|
2658
|
+
function getFieldCapability(field, type, capabilityKey, language) {
|
|
2659
|
+
const capabilities = field.capabilities;
|
|
2660
|
+
let prefix = `urn:alm:descriptor:${type}:${capabilityKey}`;
|
|
2661
|
+
if (language) {
|
|
2662
|
+
prefix += `:${language}:`;
|
|
2663
|
+
}
|
|
2664
|
+
else {
|
|
2665
|
+
prefix += ':';
|
|
2666
|
+
}
|
|
2667
|
+
const capability = capabilities?.find(c => c.startsWith(prefix));
|
|
2668
|
+
return capability?.slice(prefix.length);
|
|
2669
|
+
}
|
|
2670
|
+
function getFieldLabel(field, type) {
|
|
2671
|
+
return {
|
|
2672
|
+
en: getFieldCapability(field, type, 'label', 'en'),
|
|
2673
|
+
zh: getFieldCapability(field, type, 'label', 'zh'),
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
function getFieldDescription(field, type) {
|
|
2677
|
+
return {
|
|
2678
|
+
en: getFieldCapability(field, type, 'description', 'en'),
|
|
2679
|
+
zh: getFieldCapability(field, type, 'description', 'zh'),
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
function getFieldPlaceholder(field, type) {
|
|
2683
|
+
return {
|
|
2684
|
+
en: getFieldCapability(field, type, 'placeholder', 'en'),
|
|
2685
|
+
zh: getFieldCapability(field, type, 'placeholder', 'zh'),
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
function getUsernameDefaultValue(field) {
|
|
2689
|
+
return getFieldCapability(field, 'username', 'default');
|
|
2690
|
+
}
|
|
2691
|
+
function isFieldDisabled(field, type) {
|
|
2692
|
+
const capabilities = field.capabilities;
|
|
2693
|
+
return !!capabilities?.find(c => c === `urn:alm:descriptor:${type}:disabled`);
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
class HtpasswdFormComponent extends BaseResourceFormComponent {
|
|
2697
|
+
constructor() {
|
|
2698
|
+
super(...arguments);
|
|
2699
|
+
this.isDisabled = false;
|
|
2700
|
+
// `null` is required because the default value is `null`,
|
|
2701
|
+
// if it is `undefined` here, it will cause form value change event unexpectedly...
|
|
2702
|
+
this.originalResource = null;
|
|
2703
|
+
this.getFieldDescription = getFieldDescription;
|
|
2704
|
+
this.isFieldDisabled = isFieldDisabled;
|
|
2705
|
+
this.getFieldPlaceholder = getFieldPlaceholder;
|
|
2706
|
+
this.getFieldLabel = getFieldLabel;
|
|
2707
|
+
}
|
|
2708
|
+
createForm() {
|
|
2709
|
+
return this.fb.group({
|
|
2710
|
+
username: '',
|
|
2711
|
+
password: '',
|
|
2712
|
+
});
|
|
2713
|
+
}
|
|
2714
|
+
adaptResourceModel(resource) {
|
|
2715
|
+
let username = '';
|
|
2716
|
+
if (resource) {
|
|
2717
|
+
const parts = resource.split(':');
|
|
2718
|
+
parts.pop();
|
|
2719
|
+
username = parts.join(':');
|
|
2720
|
+
if (!this.originalUsername) {
|
|
2721
|
+
this.originalUsername = username;
|
|
2722
|
+
this.originalResource = resource;
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
return {
|
|
2726
|
+
username,
|
|
2727
|
+
password: '',
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
adaptFormModel({ username, password, }) {
|
|
2731
|
+
if (!username || !password || !this.form.get('password').dirty) {
|
|
2732
|
+
return this.originalResource;
|
|
2733
|
+
}
|
|
2734
|
+
return [username, hashSync(password)].join(':');
|
|
2735
|
+
}
|
|
2736
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2737
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: HtpasswdFormComponent, isStandalone: true, selector: "acl-htpasswd-form", inputs: { field: "field", required: "required", isUpdate: "isUpdate", isDisabled: "isDisabled" }, usesInheritance: true, ngImport: i0, template: `<ng-container [formGroup]="form">
|
|
2738
|
+
@if (
|
|
2739
|
+
(field | pure: getFieldLabel : 'username' | translate) || 'username'
|
|
2740
|
+
| translate;
|
|
2741
|
+
as label
|
|
2742
|
+
) {
|
|
2743
|
+
<aui-form-item width="large">
|
|
2744
|
+
<label auiFormItemLabel>{{ label }}</label>
|
|
2745
|
+
<input
|
|
2746
|
+
aui-input
|
|
2747
|
+
auiFormItemControl
|
|
2748
|
+
formControlName="username"
|
|
2749
|
+
[required]="required"
|
|
2750
|
+
aclErrorsMapper
|
|
2751
|
+
[aclErrorsMapperOutlet]="usernameError"
|
|
2752
|
+
[aclReadonlyField]="
|
|
2753
|
+
isDisabled || (field | pure: isFieldDisabled : 'username')
|
|
2754
|
+
"
|
|
2755
|
+
[placeholder]="
|
|
2756
|
+
field | pure: getFieldPlaceholder : 'username' | translate
|
|
2757
|
+
"
|
|
2758
|
+
/>
|
|
2759
|
+
<div
|
|
2760
|
+
#usernameError
|
|
2761
|
+
auiFormItemError
|
|
2762
|
+
></div>
|
|
2763
|
+
@if (
|
|
2764
|
+
field | pure: getFieldDescription : 'username' | translate;
|
|
2765
|
+
as description
|
|
2766
|
+
) {
|
|
2767
|
+
<div auiFormItemHint>
|
|
2768
|
+
{{ description }}
|
|
2769
|
+
</div>
|
|
2770
|
+
}
|
|
2771
|
+
</aui-form-item>
|
|
2772
|
+
}
|
|
2773
|
+
@if (
|
|
2774
|
+
(field | pure: getFieldLabel : 'password' | translate) || 'password'
|
|
2775
|
+
| translate;
|
|
2776
|
+
as label
|
|
2777
|
+
) {
|
|
2778
|
+
<aui-form-item width="large">
|
|
2779
|
+
<label auiFormItemLabel>{{ label }}</label>
|
|
2780
|
+
<input
|
|
2781
|
+
aui-input
|
|
2782
|
+
type="password"
|
|
2783
|
+
auiFormItemControl
|
|
2784
|
+
formControlName="password"
|
|
2785
|
+
aclStrongPassword
|
|
2786
|
+
[required]="
|
|
2787
|
+
(!isUpdate || originalUsername !== form.get('username').value) &&
|
|
2788
|
+
required
|
|
2789
|
+
"
|
|
2790
|
+
aclErrorsMapper
|
|
2791
|
+
[aclErrorsMapperOutlet]="passwordError"
|
|
2792
|
+
[aclReadonlyField]="
|
|
2793
|
+
isDisabled || (field | pure: isFieldDisabled : 'password')
|
|
2794
|
+
"
|
|
2795
|
+
[aclReadonlyFieldTemplate]="passwordTemplate"
|
|
2796
|
+
[placeholder]="
|
|
2797
|
+
field | pure: getFieldPlaceholder : 'password' | translate
|
|
2798
|
+
"
|
|
2799
|
+
/>
|
|
2800
|
+
<div
|
|
2801
|
+
#passwordError
|
|
2802
|
+
auiFormItemError
|
|
2803
|
+
></div>
|
|
2804
|
+
@if (
|
|
2805
|
+
field | pure: getFieldDescription : 'password' | translate;
|
|
2806
|
+
as description
|
|
2807
|
+
) {
|
|
2808
|
+
<div auiFormItemHint>
|
|
2809
|
+
{{ description }}
|
|
2810
|
+
</div>
|
|
2811
|
+
}
|
|
2812
|
+
</aui-form-item>
|
|
2813
|
+
}
|
|
2814
|
+
<ng-template #passwordTemplate> ****** </ng-template>
|
|
2815
|
+
</ng-container>`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormModule }, { kind: "component", type: i2.FormItemComponent, selector: "aui-form-item", inputs: ["labelWidth", "width", "labelPosition", "emptyAddon", "plain"] }, { kind: "directive", type: i2.FormItemErrorDirective, selector: "[auiFormItemError]" }, { kind: "directive", type: i2.FormItemHintDirective, selector: "[auiFormItemHint]" }, { kind: "directive", type: i2.FormItemLabelDirective, selector: "label[auiFormItemLabel]" }, { kind: "directive", type: i2.FormItemControlDirective, selector: "[auiFormItemControl]", inputs: ["required"] }, { kind: "ngmodule", type: InputModule }, { kind: "component", type: i2.InputComponent, selector: "input[aui-input],textarea[aui-input]", inputs: ["size", "disabled"] }, { kind: "directive", type: ErrorsMapperDirective, selector: "[aclErrorsMapper]", inputs: ["aclErrorsMapper", "aclErrorsMapperFn", "aclErrorsMapperDisabled", "aclErrorsMapperOutlet", "aclErrorsMapperControlName"] }, { kind: "directive", type: ReadonlyFieldDirective, selector: "[aclReadonlyField]", inputs: ["aclReadonlyField", "hidden", "aclReadonlyFieldTemplate", "aclReadonlyFieldTemplateContext"] }, { kind: "directive", type: StrongPasswordDirective, selector: "input[aclStrongPassword][ngModel],input[aclStrongPassword][formControl],input[aclStrongPassword][formControlName]", inputs: ["aclStrongPassword", "required", "specialChars", "minlength", "maxlength", "notStartsWith"] }, { kind: "pipe", type: PurePipe, name: "pure" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2816
|
+
}
|
|
2817
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdFormComponent, decorators: [{
|
|
2818
|
+
type: Component,
|
|
2819
|
+
args: [{
|
|
2820
|
+
selector: 'acl-htpasswd-form',
|
|
2821
|
+
template: `<ng-container [formGroup]="form">
|
|
2822
|
+
@if (
|
|
2823
|
+
(field | pure: getFieldLabel : 'username' | translate) || 'username'
|
|
2824
|
+
| translate;
|
|
2825
|
+
as label
|
|
2826
|
+
) {
|
|
2827
|
+
<aui-form-item width="large">
|
|
2828
|
+
<label auiFormItemLabel>{{ label }}</label>
|
|
2829
|
+
<input
|
|
2830
|
+
aui-input
|
|
2831
|
+
auiFormItemControl
|
|
2832
|
+
formControlName="username"
|
|
2833
|
+
[required]="required"
|
|
2834
|
+
aclErrorsMapper
|
|
2835
|
+
[aclErrorsMapperOutlet]="usernameError"
|
|
2836
|
+
[aclReadonlyField]="
|
|
2837
|
+
isDisabled || (field | pure: isFieldDisabled : 'username')
|
|
2838
|
+
"
|
|
2839
|
+
[placeholder]="
|
|
2840
|
+
field | pure: getFieldPlaceholder : 'username' | translate
|
|
2841
|
+
"
|
|
2842
|
+
/>
|
|
2843
|
+
<div
|
|
2844
|
+
#usernameError
|
|
2845
|
+
auiFormItemError
|
|
2846
|
+
></div>
|
|
2847
|
+
@if (
|
|
2848
|
+
field | pure: getFieldDescription : 'username' | translate;
|
|
2849
|
+
as description
|
|
2850
|
+
) {
|
|
2851
|
+
<div auiFormItemHint>
|
|
2852
|
+
{{ description }}
|
|
2853
|
+
</div>
|
|
2854
|
+
}
|
|
2855
|
+
</aui-form-item>
|
|
2856
|
+
}
|
|
2857
|
+
@if (
|
|
2858
|
+
(field | pure: getFieldLabel : 'password' | translate) || 'password'
|
|
2859
|
+
| translate;
|
|
2860
|
+
as label
|
|
2861
|
+
) {
|
|
2862
|
+
<aui-form-item width="large">
|
|
2863
|
+
<label auiFormItemLabel>{{ label }}</label>
|
|
2864
|
+
<input
|
|
2865
|
+
aui-input
|
|
2866
|
+
type="password"
|
|
2867
|
+
auiFormItemControl
|
|
2868
|
+
formControlName="password"
|
|
2869
|
+
aclStrongPassword
|
|
2870
|
+
[required]="
|
|
2871
|
+
(!isUpdate || originalUsername !== form.get('username').value) &&
|
|
2872
|
+
required
|
|
2873
|
+
"
|
|
2874
|
+
aclErrorsMapper
|
|
2875
|
+
[aclErrorsMapperOutlet]="passwordError"
|
|
2876
|
+
[aclReadonlyField]="
|
|
2877
|
+
isDisabled || (field | pure: isFieldDisabled : 'password')
|
|
2878
|
+
"
|
|
2879
|
+
[aclReadonlyFieldTemplate]="passwordTemplate"
|
|
2880
|
+
[placeholder]="
|
|
2881
|
+
field | pure: getFieldPlaceholder : 'password' | translate
|
|
2882
|
+
"
|
|
2883
|
+
/>
|
|
2884
|
+
<div
|
|
2885
|
+
#passwordError
|
|
2886
|
+
auiFormItemError
|
|
2887
|
+
></div>
|
|
2888
|
+
@if (
|
|
2889
|
+
field | pure: getFieldDescription : 'password' | translate;
|
|
2890
|
+
as description
|
|
2891
|
+
) {
|
|
2892
|
+
<div auiFormItemHint>
|
|
2893
|
+
{{ description }}
|
|
2894
|
+
</div>
|
|
2895
|
+
}
|
|
2896
|
+
</aui-form-item>
|
|
2897
|
+
}
|
|
2898
|
+
<ng-template #passwordTemplate> ****** </ng-template>
|
|
2899
|
+
</ng-container>`,
|
|
2900
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2901
|
+
standalone: true,
|
|
2902
|
+
imports: [
|
|
2903
|
+
ReactiveFormsModule,
|
|
2904
|
+
FormModule,
|
|
2905
|
+
InputModule,
|
|
2906
|
+
ErrorsMapperDirective,
|
|
2907
|
+
ReadonlyFieldDirective,
|
|
2908
|
+
StrongPasswordDirective,
|
|
2909
|
+
PurePipe,
|
|
2910
|
+
TranslatePipe,
|
|
2911
|
+
],
|
|
2912
|
+
}]
|
|
2913
|
+
}], propDecorators: { field: [{
|
|
2914
|
+
type: Input
|
|
2915
|
+
}], required: [{
|
|
2916
|
+
type: Input
|
|
2917
|
+
}], isUpdate: [{
|
|
2918
|
+
type: Input
|
|
2919
|
+
}], isDisabled: [{
|
|
2920
|
+
type: Input
|
|
2921
|
+
}] } });
|
|
2922
|
+
|
|
2923
|
+
class HtpasswdComponent {
|
|
2924
|
+
constructor() {
|
|
2925
|
+
this.fieldComponent = inject(forwardRef(() => OperandFieldComponent));
|
|
2926
|
+
this.isUpdate =
|
|
2927
|
+
!!this.fieldComponent.control.value && !this.fieldComponent.control.dirty;
|
|
2928
|
+
const defaultUsername = getUsernameDefaultValue(this.fieldComponent.field);
|
|
2929
|
+
if (defaultUsername && !this.isUpdate) {
|
|
2930
|
+
this.fieldComponent.control.setValue(`${defaultUsername}:`);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2934
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: HtpasswdComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `<acl-htpasswd-form
|
|
2935
|
+
[isDisabled]="fieldComponent.isDisabled"
|
|
2936
|
+
[formControl]="fieldComponent.control"
|
|
2937
|
+
[field]="fieldComponent.field"
|
|
2938
|
+
[required]="fieldComponent.isRequired"
|
|
2939
|
+
[isUpdate]="isUpdate"
|
|
2940
|
+
></acl-htpasswd-form>`, isInline: true, dependencies: [{ kind: "component", type: HtpasswdFormComponent, selector: "acl-htpasswd-form", inputs: ["field", "required", "isUpdate", "isDisabled"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2941
|
+
}
|
|
2942
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdComponent, decorators: [{
|
|
2943
|
+
type: Component,
|
|
2944
|
+
args: [{
|
|
2945
|
+
template: `<acl-htpasswd-form
|
|
2946
|
+
[isDisabled]="fieldComponent.isDisabled"
|
|
2947
|
+
[formControl]="fieldComponent.control"
|
|
2948
|
+
[field]="fieldComponent.field"
|
|
2949
|
+
[required]="fieldComponent.isRequired"
|
|
2950
|
+
[isUpdate]="isUpdate"
|
|
2951
|
+
></acl-htpasswd-form>`,
|
|
2952
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2953
|
+
standalone: true,
|
|
2954
|
+
imports: [HtpasswdFormComponent, ReactiveFormsModule],
|
|
2955
|
+
}]
|
|
2956
|
+
}], ctorParameters: () => [] });
|
|
2957
|
+
|
|
2958
|
+
const COMPONENTS = [HtpasswdComponent, HtpasswdFormComponent];
|
|
2959
|
+
class HtpasswdModule {
|
|
2960
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
2961
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdModule, imports: [HtpasswdComponent, HtpasswdFormComponent], exports: [HtpasswdComponent, HtpasswdFormComponent] }); }
|
|
2962
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdModule, imports: [COMPONENTS] }); }
|
|
2963
|
+
}
|
|
2964
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: HtpasswdModule, decorators: [{
|
|
2965
|
+
type: NgModule,
|
|
2966
|
+
args: [{
|
|
2967
|
+
imports: [...COMPONENTS],
|
|
2968
|
+
exports: COMPONENTS,
|
|
2969
|
+
}]
|
|
2970
|
+
}] });
|
|
2971
|
+
|
|
2972
|
+
const capabilityComponents = Map$1()
|
|
2973
|
+
.set(SpecCapability.resourceRequirements, SpecCapability.resourceRequirements)
|
|
2974
|
+
.set(SpecCapability.select, SpecCapability.resourceRequirements);
|
|
2975
|
+
const capabilityFor = (specCapability) => {
|
|
2976
|
+
if (isEmpty(specCapability)) {
|
|
2977
|
+
return SpecCapability.text;
|
|
2978
|
+
}
|
|
2979
|
+
return specCapability;
|
|
2980
|
+
};
|
|
2981
|
+
|
|
2982
|
+
class SpecCapabilityComponent {
|
|
2983
|
+
constructor() {
|
|
2984
|
+
this.fields = [];
|
|
2985
|
+
this.SpecCapability = SpecCapability;
|
|
2986
|
+
this.getFieldDisplayName = getFieldDisplayName;
|
|
2987
|
+
this.getFieldDescription = getFieldDescription$1;
|
|
2988
|
+
this.yamlReadOptions = yamlReadOptions;
|
|
2989
|
+
this.viewActions = viewActions;
|
|
2990
|
+
this.isArrayField = isArrayField;
|
|
2991
|
+
}
|
|
2992
|
+
get isHidden() {
|
|
2993
|
+
return this.shouldBeHidden();
|
|
2994
|
+
}
|
|
2995
|
+
getCapability(capabilities) {
|
|
2996
|
+
const capability = capabilities.find(c => c.startsWith(SpecCapability.resourceRequirements) ||
|
|
2997
|
+
c.startsWith(SpecCapability.booleanSwitch) ||
|
|
2998
|
+
c.startsWith(SpecCapability.yaml) ||
|
|
2999
|
+
c.startsWith(SpecCapability.object) ||
|
|
3000
|
+
c.startsWith(SpecCapability.array));
|
|
3001
|
+
return capabilityFor(capability);
|
|
3002
|
+
}
|
|
3003
|
+
toDefault(input) {
|
|
3004
|
+
return input ? toString(input) : '-';
|
|
3005
|
+
}
|
|
3006
|
+
shouldBeHidden() {
|
|
3007
|
+
if (this.field.capabilities.some(c => c.startsWith(SpecCapability.fieldDependency))) {
|
|
3008
|
+
const dependency = this.field.capabilities
|
|
3009
|
+
.find(c => c.startsWith(SpecCapability.fieldDependency))
|
|
3010
|
+
.split(SpecCapability.fieldDependency)[1];
|
|
3011
|
+
const [path, value] = dependency.split(':');
|
|
3012
|
+
const dependencyField = this.fields.find(f => f.path === normalizePath(path));
|
|
3013
|
+
const dependencyDefaultValue = getDefaultValue(dependencyField);
|
|
3014
|
+
return ((get(this.data, dependencyField.path) ?? dependencyDefaultValue) !==
|
|
3015
|
+
convertValue(value, dependencyField.type));
|
|
3016
|
+
}
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SpecCapabilityComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3020
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: SpecCapabilityComponent, isStandalone: true, selector: "acl-spec-descriptor-capability", inputs: { field: "field", fields: "fields", data: "data" }, host: { properties: { "class.hidden": "this.isHidden" } }, ngImport: i0, template: "<ng-container *ngIf=\"field?.capabilities | pure: getCapability as type\">\n <div class=\"field-set-item__label\">\n {{ field | pure: getFieldDisplayName | translate }}\n </div>\n\n <ng-container [ngSwitch]=\"type\">\n <ng-container *ngSwitchCase=\"SpecCapability.resourceRequirements\">\n <acl-crd-resource-requirements\n [ngModel]=\"field?.value\"\n [field]=\"field\"\n [readOnly]=\"true\"\n >\n </acl-crd-resource-requirements>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"SpecCapability.booleanSwitch\">\n {{ (field.value ? 'switch_open' : 'switch_close') | translate }}\n </ng-container>\n\n <ng-container *ngSwitchCase=\"SpecCapability.yaml\">\n <acl-crd-yaml-editor\n [ngModel]=\"field?.value\"\n [actionsConfig]=\"viewActions\"\n [options]=\"yamlReadOptions\"\n >\n </acl-crd-yaml-editor>\n </ng-container>\n\n <ng-container>\n <div\n *ngSwitchDefault\n class=\"field-set-item__value field-set-item__value__overflow field-set-item__value__wrap\"\n >\n {{ field?.value | pure: toDefault }}\n </div>\n </ng-container>\n </ng-container>\n</ng-container>\n", styles: [":host{display:flex;min-height:20px;line-height:20px;align-items:flex-start;overflow:hidden;color:use-rgb(n-1)}:host.hidden{display:none}:host ::ng-deep .edit-icon{margin-left:9px;cursor:pointer}.field-set-item__label{margin-right:8px;text-align:right}.field-set-item__label:not(:empty):after{content:\":\";margin-left:2px}.field-set-item__value{min-width:0;flex:1;white-space:nowrap;display:flex}.field-set-item__value__overflow{display:block;overflow:hidden;text-overflow:ellipsis}.field-set-item__value__wrap{white-space:pre-wrap;flex-wrap:wrap}.field-set-item__label,.field-set-item__value{height:100%;align-items:flex-start}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ResourceRequirementsComponent, selector: "acl-crd-resource-requirements", inputs: ["readOnly", "field"] }, { kind: "component", type: YamlEditorComponent, selector: "acl-crd-yaml-editor", inputs: ["actionsConfig", "options"] }, { kind: "pipe", type: PurePipe, name: "pure" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3021
|
+
}
|
|
3022
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SpecCapabilityComponent, decorators: [{
|
|
3023
|
+
type: Component,
|
|
3024
|
+
args: [{ selector: 'acl-spec-descriptor-capability', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
3025
|
+
CommonModule,
|
|
3026
|
+
FormsModule,
|
|
3027
|
+
PurePipe,
|
|
3028
|
+
TranslatePipe,
|
|
3029
|
+
ResourceRequirementsComponent,
|
|
3030
|
+
YamlEditorComponent,
|
|
3031
|
+
], template: "<ng-container *ngIf=\"field?.capabilities | pure: getCapability as type\">\n <div class=\"field-set-item__label\">\n {{ field | pure: getFieldDisplayName | translate }}\n </div>\n\n <ng-container [ngSwitch]=\"type\">\n <ng-container *ngSwitchCase=\"SpecCapability.resourceRequirements\">\n <acl-crd-resource-requirements\n [ngModel]=\"field?.value\"\n [field]=\"field\"\n [readOnly]=\"true\"\n >\n </acl-crd-resource-requirements>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"SpecCapability.booleanSwitch\">\n {{ (field.value ? 'switch_open' : 'switch_close') | translate }}\n </ng-container>\n\n <ng-container *ngSwitchCase=\"SpecCapability.yaml\">\n <acl-crd-yaml-editor\n [ngModel]=\"field?.value\"\n [actionsConfig]=\"viewActions\"\n [options]=\"yamlReadOptions\"\n >\n </acl-crd-yaml-editor>\n </ng-container>\n\n <ng-container>\n <div\n *ngSwitchDefault\n class=\"field-set-item__value field-set-item__value__overflow field-set-item__value__wrap\"\n >\n {{ field?.value | pure: toDefault }}\n </div>\n </ng-container>\n </ng-container>\n</ng-container>\n", styles: [":host{display:flex;min-height:20px;line-height:20px;align-items:flex-start;overflow:hidden;color:use-rgb(n-1)}:host.hidden{display:none}:host ::ng-deep .edit-icon{margin-left:9px;cursor:pointer}.field-set-item__label{margin-right:8px;text-align:right}.field-set-item__label:not(:empty):after{content:\":\";margin-left:2px}.field-set-item__value{min-width:0;flex:1;white-space:nowrap;display:flex}.field-set-item__value__overflow{display:block;overflow:hidden;text-overflow:ellipsis}.field-set-item__value__wrap{white-space:pre-wrap;flex-wrap:wrap}.field-set-item__label,.field-set-item__value{height:100%;align-items:flex-start}\n"] }]
|
|
3032
|
+
}], propDecorators: { field: [{
|
|
3033
|
+
type: Input
|
|
3034
|
+
}], fields: [{
|
|
3035
|
+
type: Input
|
|
3036
|
+
}], data: [{
|
|
3037
|
+
type: Input
|
|
3038
|
+
}], isHidden: [{
|
|
3039
|
+
type: HostBinding,
|
|
3040
|
+
args: ['class.hidden']
|
|
3041
|
+
}] } });
|
|
3042
|
+
|
|
3043
|
+
class SpecCapabilityListComponent {
|
|
3044
|
+
constructor() {
|
|
3045
|
+
this.openApiSchemaPath = SCHEMA_PATH;
|
|
3046
|
+
this.createCapabilityField = createCapabilityField;
|
|
3047
|
+
// TODO: Need Correct Ux For Deep Object & Array Type
|
|
3048
|
+
this.excludes = (fields) => fields.filter(field => !['object', 'array'].includes(getFieldType(field) || field.type));
|
|
3049
|
+
this.debugLog = (fields) => {
|
|
3050
|
+
/* eslint-disable no-console */
|
|
3051
|
+
if (this.debug) {
|
|
3052
|
+
console.log('treeFields:', fields.treeFields);
|
|
3053
|
+
console.log('openApiFields:', fields.openApiFields);
|
|
3054
|
+
console.log('descriptorFields:', fields.descriptorFields);
|
|
3055
|
+
console.log('basicFields:', fields.basicFields);
|
|
3056
|
+
console.log('normalFields', fields.normalFields);
|
|
3057
|
+
console.log('fieldGroups:', fields.fieldGroups);
|
|
3058
|
+
console.log('arrayFieldGroups:', fields.arrayFieldGroups);
|
|
3059
|
+
console.log('advancedFields:', fields.advancedFields);
|
|
3060
|
+
}
|
|
3061
|
+
/* eslint-enable no-console */
|
|
3062
|
+
return fields;
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SpecCapabilityListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3066
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: SpecCapabilityListComponent, isStandalone: true, selector: "acl-spec-descriptor-capability-list", inputs: { descriptors: "descriptors", debug: "debug", data: "data", openApiSchema: "openApiSchema", openApiSchemaPath: "openApiSchemaPath" }, ngImport: i0, template: "@if (\n descriptors\n | pure: createCapabilityField : openApiSchema : data : openApiSchemaPath\n | pure: debugLog;\n as fields\n) {\n @for (field of fields.normalFields | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n @if (fields.fieldGroups; as groups) {\n @for (group of groups; track group) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">{{ group.groupName }}</div>\n <div class=\"group-field-capability--content\">\n @for (field of group.fieldList | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"$any(field)\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n </div>\n }\n }\n @if (fields.advancedFields; as advanced) {\n @if (advanced.length > 0) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">\n {{ 'advanced_fields' | translate }}\n </div>\n <div class=\"group-field-capability--content\">\n @for (field of advanced | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n </div>\n }\n }\n @if (fields.arrayFieldGroups; as arrayGroups) {\n <div class=\"array-group-field-capability\">\n @for (groups of arrayGroups; track groups) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">\n {{ groups.groupName | translate }}\n </div>\n @for (fieldList of groups.fieldLists; track fieldList) {\n <div class=\"group-field-capability--content\">\n @for (field of fieldList | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n}\n", styles: [":host:after{clear:both;content:\".\";display:block;width:0;height:0;visibility:hidden}:host acl-spec-descriptor-capability{margin-bottom:12px;min-height:22px}:host>acl-spec-descriptor-capability{width:100%;box-sizing:border-box}:host .group-field-capability{padding:15px 0 12px;border-top:1px dashed #ededed}:host .group-field-capability--header{font-size:16px;font-weight:500;display:flex;line-height:22px;color:rgb(var(--aui-color-n-1))}:host .group-field-capability--content acl-spec-descriptor-capability:first-child{margin-top:12px}:host .group-field-capability--content acl-spec-descriptor-capability:last-child{margin-bottom:0}:host .array-group-field-capability .group-field-capability--content{padding:8px;margin-top:8px;background-color:#fafafa;border-left:4px solid #f0f0f0}:host .array-group-field-capability .group-field-capability--content acl-spec-descriptor-capability:first-child{margin-top:0}\n"], dependencies: [{ kind: "component", type: SpecCapabilityComponent, selector: "acl-spec-descriptor-capability", inputs: ["field", "fields", "data"] }, { kind: "pipe", type: PurePipe, name: "pure" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3067
|
+
}
|
|
3068
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SpecCapabilityListComponent, decorators: [{
|
|
3069
|
+
type: Component,
|
|
3070
|
+
args: [{ selector: 'acl-spec-descriptor-capability-list', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [PurePipe, TranslatePipe, SpecCapabilityComponent], template: "@if (\n descriptors\n | pure: createCapabilityField : openApiSchema : data : openApiSchemaPath\n | pure: debugLog;\n as fields\n) {\n @for (field of fields.normalFields | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n @if (fields.fieldGroups; as groups) {\n @for (group of groups; track group) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">{{ group.groupName }}</div>\n <div class=\"group-field-capability--content\">\n @for (field of group.fieldList | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"$any(field)\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n </div>\n }\n }\n @if (fields.advancedFields; as advanced) {\n @if (advanced.length > 0) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">\n {{ 'advanced_fields' | translate }}\n </div>\n <div class=\"group-field-capability--content\">\n @for (field of advanced | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n </div>\n }\n }\n @if (fields.arrayFieldGroups; as arrayGroups) {\n <div class=\"array-group-field-capability\">\n @for (groups of arrayGroups; track groups) {\n <div class=\"group-field-capability\">\n <div class=\"group-field-capability--header\">\n {{ groups.groupName | translate }}\n </div>\n @for (fieldList of groups.fieldLists; track fieldList) {\n <div class=\"group-field-capability--content\">\n @for (field of fieldList | pure: excludes; track field) {\n <acl-spec-descriptor-capability\n [field]=\"field\"\n [fields]=\"fields.basicFields\"\n [data]=\"data\"\n ></acl-spec-descriptor-capability>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n}\n", styles: [":host:after{clear:both;content:\".\";display:block;width:0;height:0;visibility:hidden}:host acl-spec-descriptor-capability{margin-bottom:12px;min-height:22px}:host>acl-spec-descriptor-capability{width:100%;box-sizing:border-box}:host .group-field-capability{padding:15px 0 12px;border-top:1px dashed #ededed}:host .group-field-capability--header{font-size:16px;font-weight:500;display:flex;line-height:22px;color:rgb(var(--aui-color-n-1))}:host .group-field-capability--content acl-spec-descriptor-capability:first-child{margin-top:12px}:host .group-field-capability--content acl-spec-descriptor-capability:last-child{margin-bottom:0}:host .array-group-field-capability .group-field-capability--content{padding:8px;margin-top:8px;background-color:#fafafa;border-left:4px solid #f0f0f0}:host .array-group-field-capability .group-field-capability--content acl-spec-descriptor-capability:first-child{margin-top:0}\n"] }]
|
|
3071
|
+
}], propDecorators: { descriptors: [{
|
|
3072
|
+
type: Input
|
|
3073
|
+
}], debug: [{
|
|
3074
|
+
type: Input
|
|
3075
|
+
}], data: [{
|
|
3076
|
+
type: Input
|
|
3077
|
+
}], openApiSchema: [{
|
|
3078
|
+
type: Input
|
|
3079
|
+
}], openApiSchemaPath: [{
|
|
3080
|
+
type: Input
|
|
3081
|
+
}] } });
|
|
3082
|
+
|
|
3083
|
+
/**
|
|
3084
|
+
* Generated bundle index. Do not edit.
|
|
3085
|
+
*/
|
|
3086
|
+
|
|
3087
|
+
export { BaseOperandFiledArrayComponent, BasicAuthCreateSecretDialogComponent, BasicAuthSecretComponent, CrdFormArrayTableComponent, CrdFormComponent, DEFAULT_VALUE_PATTERN, DESCRIPTOR_DEFINITION_KEY, FieldControlsModule, FormItemComponent, GROUP_PATTERN, HtpasswdComponent, HtpasswdFormComponent, HtpasswdModule, K8sResourcePrefixComponent, LinkComponent, MAX_DEPTH, NamespacedResourceKind, NodeModel, OperandAdvancedFieldGroupComponent, OperandArrayFieldGroupComponent, OperandFieldComponent, OperandFieldGroupComponent, PASSWORD_REVEAL, REGEXP_CAPABILITY_VALIDATION, REGEXP_FIELD_DEPENDENCY_CAPABILITY, REGEXP_K8S_RESOURCE_CAPABILITY, REGEXP_K8S_RESOURCE_SUFFIX, REGEXP_SELECT_CAPABILITY, REGEXP_VALIDATION_SUFFIX, ResourceRequirementsComponent, SCHEMA_PATH, SpecCapability, SpecCapabilityComponent, SpecCapabilityListComponent, SpecCapabilityUIPrefix, SpecCapabilityWidgetsPrefix, Validations, YamlEditorComponent, _isNumberValue, capabilityComponents, capabilityFor, coerceBoolean, coerceNumber, convertBooleanSwitchValue, convertValue, createCapabilityField, evalInContext, fieldShouldReveal, filterMapByKey, flattenNestedProperties, getArrayGroupName, getBasicAuthSecretConfig, getBasicCapabilityType, getBooleanSwitchOptions, getCapabilitiesForOpenApiProperty, getCapabilityDisabled, getCapabilityValidations, getDefaultValue, getFieldDescription$1 as getFieldDescription, getFieldDisplayName, getFieldDocLink, getFieldPlaceholder$1 as getFieldPlaceholder, getFieldTooltip, getFieldType, getFieldsByPath, getFieldsFromDescriptor, getFieldsFromOpenApiSchema, getFormData, getGroupName, getK8sResourcePrefixOptions, getMatchArrayIndex, getMatchedCapabilityValue, getOneOfMap, getOpenApiPropertyByPath, getOperandPath, getPathFromTagPath, getPropertyDepth, getRadioOptions, getRelatedFields, getTreeFields, hasDescriptor, isAllowCreate, isArrayField, isArrayFieldTable, isClearable, isCreatable, isEmptyArray, isFieldCopyable, isFieldDescriptionAsLink, isFolded, isGroupField, isLink, isMultiple, isRemoteField, isRequired, modifyArrayFieldPathIndex, normalizePath, parseArrayPath, parseGroupDescriptor, pathToArray, repairFieldChildPathByCorrectIndex, resolveKVParams, resolveLinkOptions, setRadiosDefaultValue };
|
|
3088
|
+
//# sourceMappingURL=alauda-fe-crd-form.mjs.map
|