mirror of https://github.com/abpframework/abp.git
32 changed files with 13249 additions and 7 deletions
@ -0,0 +1,36 @@ |
|||||
|
{ |
||||
|
"extends": ["../../.eslintrc.json"], |
||||
|
"ignorePatterns": ["!**/*"], |
||||
|
"overrides": [ |
||||
|
{ |
||||
|
"files": ["*.ts"], |
||||
|
"extends": [ |
||||
|
"plugin:@nrwl/nx/angular", |
||||
|
"plugin:@angular-eslint/template/process-inline-templates" |
||||
|
], |
||||
|
"rules": { |
||||
|
"@angular-eslint/directive-selector": [ |
||||
|
"error", |
||||
|
{ |
||||
|
"type": "attribute", |
||||
|
"prefix": "abp", |
||||
|
"style": "camelCase" |
||||
|
} |
||||
|
], |
||||
|
"@angular-eslint/component-selector": [ |
||||
|
"error", |
||||
|
{ |
||||
|
"type": "element", |
||||
|
"prefix": "abp", |
||||
|
"style": "kebab-case" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"files": ["*.html"], |
||||
|
"extends": ["plugin:@nrwl/nx/angular-template"], |
||||
|
"rules": {} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<h1> @abp/ng.feature-management </h1> |
||||
|
|
||||
|
[docs.abp.io](https://docs.abp.io) |
||||
@ -0,0 +1,20 @@ |
|||||
|
module.exports = { |
||||
|
displayName: 'feature-management', |
||||
|
preset: '../../jest.preset.js', |
||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], |
||||
|
globals: { |
||||
|
'ts-jest': { |
||||
|
tsconfig: '<rootDir>/tsconfig.spec.json', |
||||
|
stringifyContentPathRegex: '\\.(html|svg)$', |
||||
|
}, |
||||
|
}, |
||||
|
coverageDirectory: '../../coverage/packages/feature-management', |
||||
|
transform: { |
||||
|
'^.+\\.(ts|js|html)$': 'jest-preset-angular', |
||||
|
}, |
||||
|
snapshotSerializers: [ |
||||
|
'jest-preset-angular/build/serializers/no-ng-attributes', |
||||
|
'jest-preset-angular/build/serializers/ng-snapshot', |
||||
|
'jest-preset-angular/build/serializers/html-comment', |
||||
|
], |
||||
|
}; |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
||||
|
"dest": "../../dist/packages/feature-management", |
||||
|
"lib": { |
||||
|
"entryFile": "src/public-api.ts" |
||||
|
}, |
||||
|
"allowedNonPeerDependencies": ["@abp/ng.theme.shared"] |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
{ |
||||
|
"name": "@abp/ng.feature-management", |
||||
|
"version": "4.4.0", |
||||
|
"homepage": "https://abp.io", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/abpframework/abp.git" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@abp/ng.theme.shared": "~4.4.0", |
||||
|
"tslib": "^2.0.0" |
||||
|
}, |
||||
|
"publishConfig": { |
||||
|
"access": "public" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,126 @@ |
|||||
|
<abp-modal *ngIf="visible" size="lg" [(visible)]="visible" [busy]="modalBusy"> |
||||
|
<ng-template #abpHeader> |
||||
|
<h3>{{ 'AbpFeatureManagement::Features' | abpLocalization }}</h3> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #abpBody> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-4"> |
||||
|
<ul |
||||
|
ngbNav |
||||
|
#nav="ngbNav" |
||||
|
[(activeId)]="selectedGroupDisplayName" |
||||
|
class="nav-pills" |
||||
|
orientation="vertical" |
||||
|
> |
||||
|
<li |
||||
|
*ngFor="let group of groups; trackBy: track.by('name')" |
||||
|
[ngbNavItem]="group.displayName" |
||||
|
> |
||||
|
<a ngbNavLink>{{ group.displayName }}</a> |
||||
|
<ng-template ngbNavContent> |
||||
|
<h4>{{ selectedGroupDisplayName }}</h4> |
||||
|
<hr class="mt-2 mb-3" /> |
||||
|
|
||||
|
<div |
||||
|
class="mt-2" |
||||
|
*ngFor="let feature of features[group.name]; let i = index; trackBy: track.by('id')" |
||||
|
[ngStyle]="feature.style" |
||||
|
[ngSwitch]="feature.valueType?.name" |
||||
|
(keyup.enter)="save()" |
||||
|
> |
||||
|
<ng-container *ngSwitchCase="valueTypes.ToggleStringValueType"> |
||||
|
<div class="custom-checkbox custom-control"> |
||||
|
<input |
||||
|
class="custom-control-input" |
||||
|
type="checkbox" |
||||
|
[id]="feature.name" |
||||
|
[(ngModel)]="feature.value" |
||||
|
(ngModelChange)="onCheckboxClick($event, feature)" |
||||
|
/> |
||||
|
|
||||
|
<label class="custom-control-label" [htmlFor]="feature.name">{{ |
||||
|
feature.displayName |
||||
|
}}</label> |
||||
|
<ng-container |
||||
|
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }" |
||||
|
></ng-container> |
||||
|
</div> |
||||
|
</ng-container> |
||||
|
<ng-container *ngSwitchCase="valueTypes.FreeTextStringValueType"> |
||||
|
<div class="form-group"> |
||||
|
<label [htmlFor]="feature.name">{{ feature.displayName }}</label> |
||||
|
<input |
||||
|
class="form-control" |
||||
|
type="text" |
||||
|
[id]="feature.name" |
||||
|
[(ngModel)]="feature.value" |
||||
|
[abpFeatureManagementFreeText]="feature" |
||||
|
/> |
||||
|
|
||||
|
<ng-container |
||||
|
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }" |
||||
|
></ng-container> |
||||
|
</div> |
||||
|
</ng-container> |
||||
|
<ng-container *ngSwitchCase="valueTypes.SelectionStringValueType"> |
||||
|
<ng-container *ngIf="feature.valueType.itemSource?.items?.length"> |
||||
|
<div class="form-group"> |
||||
|
<label [htmlFor]="feature.name">{{ feature.displayName }}</label> |
||||
|
<select |
||||
|
class="form-control custom-select" |
||||
|
[id]="feature.name" |
||||
|
[(ngModel)]="feature.value" |
||||
|
> |
||||
|
<option |
||||
|
*ngFor=" |
||||
|
let item of feature.valueType.itemSource?.items; |
||||
|
trackBy: track.by('value') |
||||
|
" |
||||
|
[ngValue]="item.value" |
||||
|
> |
||||
|
{{ |
||||
|
item.displayText?.resourceName + '::' + item.displayText?.name |
||||
|
| abpLocalization |
||||
|
}} |
||||
|
</option> |
||||
|
</select> |
||||
|
<ng-container |
||||
|
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }" |
||||
|
></ng-container> |
||||
|
</div> |
||||
|
</ng-container> |
||||
|
</ng-container> |
||||
|
<ng-container *ngSwitchDefault>{{ feature.displayName }}</ng-container> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<ng-template #descTmp let-description> |
||||
|
<small *ngIf="description" class="form-text text-muted">{{ description }}</small> |
||||
|
</ng-template> |
||||
|
|
||||
|
<div class="col-md-8"><div [ngbNavOutlet]="nav"></div></div> |
||||
|
|
||||
|
<div class="mx-3" *ngIf="!groups.length"> |
||||
|
{{ 'AbpFeatureManagement::NoFeatureFoundMessage' | abpLocalization }} |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #abpFooter> |
||||
|
<button abpClose type="button" class="btn btn-secondary"> |
||||
|
{{ 'AbpFeatureManagement::Cancel' | abpLocalization }} |
||||
|
</button> |
||||
|
<abp-button |
||||
|
*ngIf="groups.length" |
||||
|
iconClass="fa fa-check" |
||||
|
[disabled]="modalBusy" |
||||
|
(click)="save()" |
||||
|
> |
||||
|
{{ 'AbpFeatureManagement::Save' | abpLocalization }} |
||||
|
</abp-button> |
||||
|
</ng-template> |
||||
|
</abp-modal> |
||||
@ -0,0 +1,214 @@ |
|||||
|
import { |
||||
|
AbpApplicationConfigurationService, |
||||
|
ConfigStateService, |
||||
|
TrackByService, |
||||
|
} from '@abp/ng.core'; |
||||
|
import { LocaleDirection } from '@abp/ng.theme.shared'; |
||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core'; |
||||
|
import { Store } from '@ngxs/store'; |
||||
|
import { finalize, tap } from 'rxjs/operators'; |
||||
|
import { FeatureManagement } from '../../models/feature-management'; |
||||
|
import { FeaturesService } from '../../proxy/feature-management/features.service'; |
||||
|
import { |
||||
|
FeatureDto, |
||||
|
FeatureGroupDto, |
||||
|
UpdateFeatureDto, |
||||
|
} from '../../proxy/feature-management/models'; |
||||
|
|
||||
|
enum ValueTypes { |
||||
|
ToggleStringValueType = 'ToggleStringValueType', |
||||
|
FreeTextStringValueType = 'FreeTextStringValueType', |
||||
|
SelectionStringValueType = 'SelectionStringValueType', |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-feature-management', |
||||
|
templateUrl: './feature-management.component.html', |
||||
|
exportAs: 'abpFeatureManagement', |
||||
|
}) |
||||
|
export class FeatureManagementComponent |
||||
|
implements |
||||
|
FeatureManagement.FeatureManagementComponentInputs, |
||||
|
FeatureManagement.FeatureManagementComponentOutputs { |
||||
|
@Input() |
||||
|
providerKey: string; |
||||
|
|
||||
|
@Input() |
||||
|
providerName: string; |
||||
|
|
||||
|
selectedGroupDisplayName: string; |
||||
|
|
||||
|
groups: Pick<FeatureGroupDto, 'name' | 'displayName'>[] = []; |
||||
|
|
||||
|
features: { |
||||
|
[group: string]: Array<FeatureDto & { style?: { [key: string]: number }; initialValue: any }>; |
||||
|
}; |
||||
|
|
||||
|
valueTypes = ValueTypes; |
||||
|
|
||||
|
protected _visible; |
||||
|
|
||||
|
@Input() |
||||
|
get visible(): boolean { |
||||
|
return this._visible; |
||||
|
} |
||||
|
|
||||
|
set visible(value: boolean) { |
||||
|
if (this._visible === value) return; |
||||
|
|
||||
|
this._visible = value; |
||||
|
this.visibleChange.emit(value); |
||||
|
if (value) this.openModal(); |
||||
|
} |
||||
|
|
||||
|
@Output() readonly visibleChange = new EventEmitter<boolean>(); |
||||
|
|
||||
|
modalBusy = false; |
||||
|
|
||||
|
constructor( |
||||
|
public readonly track: TrackByService, |
||||
|
protected service: FeaturesService, |
||||
|
protected store: Store, |
||||
|
protected configState: ConfigStateService, |
||||
|
protected appConfigService: AbpApplicationConfigurationService, |
||||
|
) {} |
||||
|
|
||||
|
openModal() { |
||||
|
if (!this.providerName) { |
||||
|
throw new Error('providerName is required.'); |
||||
|
} |
||||
|
|
||||
|
this.getFeatures(); |
||||
|
} |
||||
|
|
||||
|
getFeatures() { |
||||
|
this.service.get(this.providerName, this.providerKey).subscribe(res => { |
||||
|
if (!res.groups?.length) return; |
||||
|
this.groups = res.groups.map(({ name, displayName }) => ({ name, displayName })); |
||||
|
this.selectedGroupDisplayName = this.groups[0].displayName; |
||||
|
this.features = res.groups.reduce( |
||||
|
(acc, val) => ({ |
||||
|
...acc, |
||||
|
[val.name]: mapFeatures(val.features, document.body.dir as LocaleDirection), |
||||
|
}), |
||||
|
{}, |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
save() { |
||||
|
if (this.modalBusy) return; |
||||
|
|
||||
|
const changedFeatures = [] as UpdateFeatureDto[]; |
||||
|
|
||||
|
Object.keys(this.features).forEach(key => { |
||||
|
this.features[key].forEach(feature => { |
||||
|
if (feature.value !== feature.initialValue) |
||||
|
changedFeatures.push({ name: feature.name, value: `${feature.value}` }); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
if (!changedFeatures.length) { |
||||
|
this.visible = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.modalBusy = true; |
||||
|
this.service |
||||
|
.update(this.providerName, this.providerKey, { features: changedFeatures }) |
||||
|
.pipe(finalize(() => (this.modalBusy = false))) |
||||
|
.subscribe(() => { |
||||
|
this.visible = false; |
||||
|
|
||||
|
if (!this.providerKey) { |
||||
|
// to refresh host's features
|
||||
|
this.appConfigService |
||||
|
.get() |
||||
|
.pipe(tap(res => this.configState.setState(res))) |
||||
|
.subscribe(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onCheckboxClick(val: boolean, feature: FeatureDto) { |
||||
|
if (val) { |
||||
|
this.checkToggleAncestors(feature); |
||||
|
} else { |
||||
|
this.uncheckToggleDescendants(feature); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private uncheckToggleDescendants(feature: FeatureDto) { |
||||
|
this.findAllDescendantsOfByType(feature, ValueTypes.ToggleStringValueType).forEach(node => |
||||
|
this.setFeatureValue(node, false), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private checkToggleAncestors(feature: FeatureDto) { |
||||
|
this.findAllAncestorsOfByType(feature, ValueTypes.ToggleStringValueType).forEach(node => |
||||
|
this.setFeatureValue(node, true), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private findAllAncestorsOfByType(feature: FeatureDto, type: ValueTypes) { |
||||
|
let parent = this.findParentByType(feature, type); |
||||
|
const ancestors = []; |
||||
|
while (parent) { |
||||
|
ancestors.push(parent); |
||||
|
parent = this.findParentByType(parent, type); |
||||
|
} |
||||
|
return ancestors; |
||||
|
} |
||||
|
|
||||
|
private findAllDescendantsOfByType(feature: FeatureDto, type: ValueTypes) { |
||||
|
const descendants = []; |
||||
|
const queue = [feature]; |
||||
|
|
||||
|
while (queue.length) { |
||||
|
const node = queue.pop(); |
||||
|
const newDescendants = this.findChildrenByType(node, type); |
||||
|
descendants.push(...newDescendants); |
||||
|
queue.push(...newDescendants); |
||||
|
} |
||||
|
|
||||
|
return descendants; |
||||
|
} |
||||
|
|
||||
|
private findParentByType(feature: FeatureDto, type: ValueTypes) { |
||||
|
return this.getCurrentGroup().find( |
||||
|
f => f.valueType.name === type && f.name === feature.parentName, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private findChildrenByType(feature: FeatureDto, type: ValueTypes) { |
||||
|
return this.getCurrentGroup().filter( |
||||
|
f => f.valueType.name === type && f.parentName === feature.name, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private getCurrentGroup() { |
||||
|
return this.features[this.selectedGroupDisplayName] ?? []; |
||||
|
} |
||||
|
|
||||
|
private setFeatureValue(feature: FeatureDto, val: boolean) { |
||||
|
feature.value = val as any; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function mapFeatures(features: FeatureDto[], dir: LocaleDirection) { |
||||
|
const margin = `margin-${dir === 'rtl' ? 'right' : 'left'}.px`; |
||||
|
|
||||
|
return features.map(feature => { |
||||
|
const value = |
||||
|
feature.valueType?.name === ValueTypes.ToggleStringValueType |
||||
|
? (feature.value || '').toLowerCase() === 'true' |
||||
|
: feature.value; |
||||
|
|
||||
|
return { |
||||
|
...feature, |
||||
|
value, |
||||
|
initialValue: value, |
||||
|
style: { [margin]: feature.depth * 20 }, |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './feature-management/feature-management.component'; |
||||
@ -0,0 +1,38 @@ |
|||||
|
import { Directive, Input, HostBinding } from '@angular/core'; |
||||
|
|
||||
|
// TODO: improve this type
|
||||
|
export interface FreeTextType { |
||||
|
valueType: { |
||||
|
validator: { |
||||
|
name: string; |
||||
|
}; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export const INPUT_TYPES = { |
||||
|
numeric: 'number', |
||||
|
default: 'text', |
||||
|
}; |
||||
|
|
||||
|
@Directive({ |
||||
|
selector: 'input[abpFeatureManagementFreeText]', |
||||
|
exportAs: 'inputAbpFeatureManagementFreeText', |
||||
|
}) |
||||
|
export class FreeTextInputDirective { |
||||
|
_feature: FreeTextType; |
||||
|
@Input('abpFeatureManagementFreeText') set feature(val: FreeTextType) { |
||||
|
this._feature = val; |
||||
|
this.setInputType(); |
||||
|
} |
||||
|
|
||||
|
get feature() { |
||||
|
return this._feature; |
||||
|
} |
||||
|
|
||||
|
@HostBinding('type') type: string; |
||||
|
|
||||
|
private setInputType() { |
||||
|
const validatorType = this.feature?.valueType?.validator?.name.toLowerCase(); |
||||
|
this.type = INPUT_TYPES[validatorType] ?? INPUT_TYPES.default; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './free-text-input.directive'; |
||||
@ -0,0 +1,3 @@ |
|||||
|
export const enum eFeatureManagementComponents { |
||||
|
FeatureManagement = 'FeatureManagement.FeatureManagementComponent', |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { CoreModule } from '@abp/ng.core'; |
||||
|
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; |
||||
|
import { NgxsModule } from '@ngxs/store'; |
||||
|
import { FeatureManagementComponent } from './components/feature-management/feature-management.component'; |
||||
|
import { FreeTextInputDirective } from './directives/free-text-input.directive'; |
||||
|
|
||||
|
const exported = [FeatureManagementComponent, FreeTextInputDirective]; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [...exported], |
||||
|
imports: [CoreModule, ThemeSharedModule, NgbNavModule, NgxsModule.forFeature([])], |
||||
|
exports: [...exported], |
||||
|
}) |
||||
|
export class FeatureManagementModule {} |
||||
@ -0,0 +1,13 @@ |
|||||
|
import { EventEmitter } from '@angular/core'; |
||||
|
|
||||
|
export namespace FeatureManagement { |
||||
|
export interface FeatureManagementComponentInputs { |
||||
|
visible: boolean; |
||||
|
readonly providerName: string; |
||||
|
readonly providerKey: string; |
||||
|
} |
||||
|
|
||||
|
export interface FeatureManagementComponentOutputs { |
||||
|
readonly visibleChange: EventEmitter<boolean>; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './feature-management'; |
||||
@ -0,0 +1,13 @@ |
|||||
|
# Proxy Generation Output |
||||
|
|
||||
|
This directory includes the output of the latest proxy generation. |
||||
|
The files and folders in it will be overwritten when proxy generation is run again. |
||||
|
Therefore, please do not place your own content in this folder. |
||||
|
|
||||
|
In addition, `generate-proxy.json` works like a lock file. |
||||
|
It includes information used by the proxy generator, so please do not delete or modify it. |
||||
|
|
||||
|
Finally, the name of the files and folders should not be changed for two reasons: |
||||
|
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content. |
||||
|
- ABP Suite generates files which include imports from this folder. |
||||
|
|
||||
@ -0,0 +1,29 @@ |
|||||
|
import type { GetFeatureListResultDto, UpdateFeaturesDto } from './models'; |
||||
|
import { RestService } from '@abp/ng.core'; |
||||
|
import { Injectable } from '@angular/core'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root', |
||||
|
}) |
||||
|
export class FeaturesService { |
||||
|
apiName = 'AbpFeatureManagement'; |
||||
|
|
||||
|
get = (providerName: string, providerKey: string) => |
||||
|
this.restService.request<any, GetFeatureListResultDto>({ |
||||
|
method: 'GET', |
||||
|
url: '/api/feature-management/features', |
||||
|
params: { providerName, providerKey }, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
update = (providerName: string, providerKey: string, input: UpdateFeaturesDto) => |
||||
|
this.restService.request<any, void>({ |
||||
|
method: 'PUT', |
||||
|
url: '/api/feature-management/features', |
||||
|
params: { providerName, providerKey }, |
||||
|
body: input, |
||||
|
}, |
||||
|
{ apiName: this.apiName }); |
||||
|
|
||||
|
constructor(private restService: RestService) {} |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './features.service'; |
||||
|
export * from './models'; |
||||
@ -0,0 +1,36 @@ |
|||||
|
import type { IStringValueType } from '../validation/string-values/models'; |
||||
|
|
||||
|
export interface FeatureDto { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
value: string; |
||||
|
provider: FeatureProviderDto; |
||||
|
description: string; |
||||
|
valueType: IStringValueType; |
||||
|
depth: number; |
||||
|
parentName: string; |
||||
|
} |
||||
|
|
||||
|
export interface FeatureGroupDto { |
||||
|
name: string; |
||||
|
displayName: string; |
||||
|
features: FeatureDto[]; |
||||
|
} |
||||
|
|
||||
|
export interface FeatureProviderDto { |
||||
|
name: string; |
||||
|
key: string; |
||||
|
} |
||||
|
|
||||
|
export interface GetFeatureListResultDto { |
||||
|
groups: FeatureGroupDto[]; |
||||
|
} |
||||
|
|
||||
|
export interface UpdateFeatureDto { |
||||
|
name: string; |
||||
|
value: string; |
||||
|
} |
||||
|
|
||||
|
export interface UpdateFeaturesDto { |
||||
|
features: UpdateFeatureDto[]; |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,2 @@ |
|||||
|
export * as FeatureManagement from './feature-management'; |
||||
|
export * as Validation from './validation'; |
||||
@ -0,0 +1 @@ |
|||||
|
export * as StringValues from './string-values'; |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './models'; |
||||
@ -0,0 +1,13 @@ |
|||||
|
|
||||
|
export interface IStringValueType { |
||||
|
name: string; |
||||
|
item: object; |
||||
|
properties: Record<string, object>; |
||||
|
validator: IValueValidator; |
||||
|
} |
||||
|
|
||||
|
export interface IValueValidator { |
||||
|
name: string; |
||||
|
item: object; |
||||
|
properties: Record<string, object>; |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
export * from './lib/feature-management.module'; |
||||
|
export * from './lib/components'; |
||||
|
export * from './lib/enums/components'; |
||||
|
export * from './lib/proxy/feature-management'; |
||||
|
export * from './lib/proxy/validation/string-values'; |
||||
@ -0,0 +1 @@ |
|||||
|
import 'jest-preset-angular/setup-jest'; |
||||
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"extends": "../../tsconfig.base.json", |
||||
|
"files": [], |
||||
|
"include": [], |
||||
|
"references": [ |
||||
|
{ |
||||
|
"path": "./tsconfig.lib.json" |
||||
|
}, |
||||
|
{ |
||||
|
"path": "./tsconfig.spec.json" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"extends": "./tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../../dist/out-tsc", |
||||
|
"target": "es2015", |
||||
|
"declaration": true, |
||||
|
"declarationMap": true, |
||||
|
"inlineSources": true, |
||||
|
"types": [], |
||||
|
"lib": ["dom", "es2018"] |
||||
|
}, |
||||
|
"exclude": ["src/test-setup.ts", "**/*.spec.ts"], |
||||
|
"include": ["**/*.ts"] |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
||||
|
{ |
||||
|
"extends": "./tsconfig.lib.json", |
||||
|
"compilerOptions": { |
||||
|
"declarationMap": false |
||||
|
}, |
||||
|
"angularCompilerOptions": { |
||||
|
"compilationMode": "partial", |
||||
|
"enableIvy": false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
{ |
||||
|
"extends": "./tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../../dist/out-tsc", |
||||
|
"module": "commonjs", |
||||
|
"types": ["jest", "node"] |
||||
|
}, |
||||
|
"files": ["src/test-setup.ts"], |
||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"] |
||||
|
} |
||||
Loading…
Reference in new issue