@ng-formworks/cssframework 15.2.7
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 +28 -0
- package/ng-package.json +12 -0
- package/package.json +51 -0
- package/src/lib/css-framework.component.html +67 -0
- package/src/lib/css-framework.component.scss +45 -0
- package/src/lib/css-framework.component.spec.ts +21 -0
- package/src/lib/css-framework.component.ts +304 -0
- package/src/lib/css-framework.defs.ts +78 -0
- package/src/lib/css-framework.module.ts +29 -0
- package/src/lib/css-framework.service.spec.ts +16 -0
- package/src/lib/css-framework.service.ts +33 -0
- package/src/lib/css.framework.ts +99 -0
- package/src/public-api.ts +10 -0
- package/tsconfig.lib.json +14 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @ng-formworks/cssframework
|
|
2
|
+
|
|
3
|
+
This module is a dependency of the [ng-formworks project][npm_core_ver] and not intended to be consumed independently see [@ng-formworks/core][npm_core_ver] for more details.
|
|
4
|
+
Below is information regarding the development tasks and not related to the module's installation, it should automatically be installed when a framework module is installed.
|
|
5
|
+
|
|
6
|
+
## Code scaffolding
|
|
7
|
+
|
|
8
|
+
Run `ng generate component component-name --project @ng-formworks/cssframework` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project @ng-formworks/cssframework`.
|
|
9
|
+
> Note: Don't forget to add `--project @ng-formworks/cssframework` or else it will be added to the default project in your `angular.json` file.
|
|
10
|
+
|
|
11
|
+
## Build
|
|
12
|
+
|
|
13
|
+
Run `ng build @ng-formworks/cssframework` to build the project. The build artifacts will be stored in the `dist/` directory.
|
|
14
|
+
|
|
15
|
+
## Publishing
|
|
16
|
+
|
|
17
|
+
After building your library with `ng build @ng-formworks/cssframework`, go to the dist folder `cd dist/@ng-formworks/cssframework` and run `npm publish`.
|
|
18
|
+
|
|
19
|
+
## Running unit tests
|
|
20
|
+
|
|
21
|
+
Run `ng test @ng-formworks/cssframework` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
22
|
+
|
|
23
|
+
## Further help
|
|
24
|
+
|
|
25
|
+
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[npm_core_ver]:https://www.npmjs.com/package/@ng-formworks/core
|
package/ng-package.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
|
3
|
+
"dest": "../../dist/@ng-formworks/cssframework",
|
|
4
|
+
"lib": {
|
|
5
|
+
"entryFile": "src/public-api.ts"
|
|
6
|
+
},
|
|
7
|
+
"assets": ["./assets"],
|
|
8
|
+
"allowedNonPeerDependencies": [
|
|
9
|
+
"lodash-es",
|
|
10
|
+
"@ng-formworks/core"
|
|
11
|
+
]
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ng-formworks/cssframework",
|
|
3
|
+
"version": "15.2.7",
|
|
4
|
+
"description": "Angular ng-formworks - JSON Schema Form builder cssframework",
|
|
5
|
+
"author": "https://github.com/zahmo/ng-formworks/graphs/contributors",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"Angular",
|
|
8
|
+
"ng",
|
|
9
|
+
"Angular15",
|
|
10
|
+
"Angular 15",
|
|
11
|
+
"Angular16",
|
|
12
|
+
"Angular 16",
|
|
13
|
+
"Angular17",
|
|
14
|
+
"Angular 17",
|
|
15
|
+
"ng15",
|
|
16
|
+
"ng16",
|
|
17
|
+
"ng17",
|
|
18
|
+
"JSON Schema",
|
|
19
|
+
"form",
|
|
20
|
+
"forms",
|
|
21
|
+
"form builder",
|
|
22
|
+
"form themes",
|
|
23
|
+
"form generator",
|
|
24
|
+
"ajsf",
|
|
25
|
+
"ng-formworks",
|
|
26
|
+
"ng formworks",
|
|
27
|
+
"formworks",
|
|
28
|
+
"angular json schema form"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/zahmo/ng-formworks"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/zahmo/ng-formworks/issues"
|
|
37
|
+
},
|
|
38
|
+
"private": false,
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@angular/common": "^17.0.0",
|
|
41
|
+
"@angular/core": "^17.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"tslib": "^2.3.0",
|
|
45
|
+
"@ng-formworks/core": "~15.2.7"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/lodash-es": "^4.17.6"
|
|
49
|
+
},
|
|
50
|
+
"sideEffects": false
|
|
51
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<div [attr.data-bs-theme]="theme" [attr.data-theme]="theme" [class]="options?.htmlClass || ''" [class.has-feedback]="options?.feedback && options?.isInputWidget &&
|
|
2
|
+
(formControl?.dirty || options?.feedbackOnRender)" [class.has-error]="options?.enableErrorState && formControl?.errors &&
|
|
3
|
+
(formControl?.dirty || options?.feedbackOnRender)" [class.has-success]="options?.enableSuccessState && !formControl?.errors &&
|
|
4
|
+
(formControl?.dirty || options?.feedbackOnRender)">
|
|
5
|
+
|
|
6
|
+
<button *ngIf="showRemoveButton" [class]="widgetStyles.__remove_item__" type="button" (click)="removeItem()">
|
|
7
|
+
<span aria-hidden="true">×</span>
|
|
8
|
+
<span [class]="widgetStyles.__screen_reader__">Close</span>
|
|
9
|
+
</button>
|
|
10
|
+
<div *ngIf="options?.messageLocation === 'top'">
|
|
11
|
+
<p *ngIf="options?.helpBlock" [class]="widgetStyles.__help_block__" [innerHTML]="options?.helpBlock"></p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<label *ngIf="options?.title && layoutNode?.type !== 'tab'" [attr.for]="'control' + layoutNode?._id" [class]="options?.labelHtmlClass || ''" [class.sr-only]="options?.notitle" [innerHTML]="options?.title"></label>
|
|
15
|
+
<p *ngIf="layoutNode?.type === 'submit' && jsf?.formOptions?.fieldsRequired">
|
|
16
|
+
<strong [class]="widgetStyles.__required_asterisk__">*</strong> = required fields
|
|
17
|
+
</p>
|
|
18
|
+
<div [class.input-group]="options?.fieldAddonLeft || options?.fieldAddonRight">
|
|
19
|
+
<span *ngIf="options?.fieldAddonLeft" [class]="widgetStyles.__field_addon_left__" [innerHTML]="options?.fieldAddonLeft"></span>
|
|
20
|
+
|
|
21
|
+
<select-widget-widget [layoutNode]="widgetLayoutNode" [dataIndex]="dataIndex" [layoutIndex]="layoutIndex"></select-widget-widget>
|
|
22
|
+
|
|
23
|
+
<span *ngIf="options?.fieldAddonRight" [class]="widgetStyles.__field_addon_right__" [innerHTML]="options?.fieldAddonRight"></span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<span *ngIf="options?.feedback && options?.isInputWidget &&
|
|
27
|
+
!options?.fieldAddonRight && !layoutNode.arrayItem &&
|
|
28
|
+
(formControl?.dirty || options?.feedbackOnRender)" [class.glyphicon-ok]="options?.enableSuccessState && !formControl?.errors" [class.glyphicon-remove]="options?.enableErrorState && formControl?.errors" aria-hidden="true" class="form-control-feedback glyphicon"></span>
|
|
29
|
+
<div *ngIf="options?.messageLocation !== 'top'">
|
|
30
|
+
<p *ngIf="options?.helpBlock" [class]="widgetStyles.__help_block__" [innerHTML]="options?.helpBlock"></p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div *ngIf="debug && debugOutput">debug:
|
|
36
|
+
<pre>{{debugOutput}}</pre>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!--
|
|
40
|
+
<div class="btn input input-bordered input-primary w-full max-w-xs
|
|
41
|
+
btn-neutral
|
|
42
|
+
btn-primary
|
|
43
|
+
btn-secondary
|
|
44
|
+
btn-accent
|
|
45
|
+
btn-info
|
|
46
|
+
btn-success
|
|
47
|
+
btn-warning
|
|
48
|
+
btn-error
|
|
49
|
+
btn-ghost
|
|
50
|
+
btn-link
|
|
51
|
+
btn-outline
|
|
52
|
+
btn-active
|
|
53
|
+
btn-disabled
|
|
54
|
+
glass
|
|
55
|
+
no-animation
|
|
56
|
+
btn-lg
|
|
57
|
+
btn-md
|
|
58
|
+
btn-sm
|
|
59
|
+
btn-xs
|
|
60
|
+
btn-wide
|
|
61
|
+
btn-block
|
|
62
|
+
btn-circle
|
|
63
|
+
btn-square
|
|
64
|
+
hidden
|
|
65
|
+
">defs al</div>
|
|
66
|
+
-->
|
|
67
|
+
<!--<input type="text" placeholder="Type here" class="input input-bordered input-primary w-full max-w-xs" />-->
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
:host ::ng-deep {
|
|
2
|
+
.list-group-item .form-control-feedback {
|
|
3
|
+
top: 40px;
|
|
4
|
+
}
|
|
5
|
+
.checkbox,
|
|
6
|
+
.radio {
|
|
7
|
+
margin-top: 0;
|
|
8
|
+
margin-bottom: 0;
|
|
9
|
+
}
|
|
10
|
+
.checkbox-inline,
|
|
11
|
+
.checkbox-inline+.checkbox-inline,
|
|
12
|
+
.checkbox-inline+.radio-inline,
|
|
13
|
+
.radio-inline,
|
|
14
|
+
.radio-inline+.radio-inline,
|
|
15
|
+
.radio-inline+.checkbox-inline {
|
|
16
|
+
margin-left: 0;
|
|
17
|
+
margin-right: 10px;
|
|
18
|
+
}
|
|
19
|
+
.checkbox-inline:last-child,
|
|
20
|
+
.radio-inline:last-child {
|
|
21
|
+
margin-right: 0;
|
|
22
|
+
}
|
|
23
|
+
.ng-invalid.ng-touched {
|
|
24
|
+
border: 1px solid #f44336;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.checkbox-inline,
|
|
29
|
+
.checkbox-inline+.checkbox-inline,
|
|
30
|
+
.checkbox-inline+.radio-inline,
|
|
31
|
+
.radio-inline,
|
|
32
|
+
.radio-inline+.radio-inline,
|
|
33
|
+
.radio-inline+.checkbox-inline {
|
|
34
|
+
margin-left: 0;
|
|
35
|
+
margin-right: 10px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.checkbox-inline:last-child,
|
|
39
|
+
.radio-inline:last-child {
|
|
40
|
+
margin-right: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ng-invalid.ng-touched {
|
|
44
|
+
border: 1px solid #f44336;
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CssFrameworkComponent } from './css-framework.component';
|
|
4
|
+
|
|
5
|
+
describe('CssFrameworkComponent', () => {
|
|
6
|
+
let component: CssFrameworkComponent;
|
|
7
|
+
let fixture: ComponentFixture<CssFrameworkComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
TestBed.configureTestingModule({
|
|
11
|
+
declarations: [CssFrameworkComponent]
|
|
12
|
+
});
|
|
13
|
+
fixture = TestBed.createComponent(CssFrameworkComponent);
|
|
14
|
+
component = fixture.componentInstance;
|
|
15
|
+
fixture.detectChanges();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should create', () => {
|
|
19
|
+
expect(component).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
|
2
|
+
import { FrameworkLibraryService, JsonSchemaFormService, addClasses, inArray } from '@ng-formworks/core';
|
|
3
|
+
import _, { cloneDeep, map } from 'lodash';
|
|
4
|
+
import { Subscription } from 'rxjs';
|
|
5
|
+
import { css_fw } from './css-framework.defs';
|
|
6
|
+
import { CssframeworkService } from './css-framework.service';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'css-framework',
|
|
10
|
+
templateUrl: './css-framework.component.html',
|
|
11
|
+
styleUrls: ['./css-framework.component.scss'],
|
|
12
|
+
encapsulation: ViewEncapsulation.None
|
|
13
|
+
})
|
|
14
|
+
export class CssFrameworkComponent implements OnInit, OnChanges,OnDestroy {
|
|
15
|
+
frameworkInitialized = false;
|
|
16
|
+
widgetOptions: any; // Options passed to child widget
|
|
17
|
+
widgetLayoutNode: any; // layoutNode passed to child widget
|
|
18
|
+
options: any; // Options used in this framework
|
|
19
|
+
formControl: any = null;
|
|
20
|
+
debugOutput: any = '';
|
|
21
|
+
debug: any = '';
|
|
22
|
+
parentArray: any = null;
|
|
23
|
+
isOrderable = false;
|
|
24
|
+
@Input() layoutNode: any;
|
|
25
|
+
@Input() layoutIndex: number[];
|
|
26
|
+
@Input() dataIndex: number[];
|
|
27
|
+
|
|
28
|
+
@Input() widgetStyles: css_fw.widgetstyles;
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
//TODO-move to ng-formworks/core utility.functions.ts
|
|
35
|
+
applyCssClasses(type, widgetOptions, styleOptions) {
|
|
36
|
+
//console.log("applyCssClasses for type:"+type);
|
|
37
|
+
let cssClasses = this.widgetStyles[type];
|
|
38
|
+
if (!cssClasses || _.isEmpty(cssClasses) ) {
|
|
39
|
+
cssClasses = this.widgetStyles.default;
|
|
40
|
+
}
|
|
41
|
+
Object.keys(cssClasses).forEach(catName => {
|
|
42
|
+
let classList = cssClasses[catName];
|
|
43
|
+
|
|
44
|
+
if (classList.length) {
|
|
45
|
+
widgetOptions[catName] = addClasses(widgetOptions[catName], classList);
|
|
46
|
+
}
|
|
47
|
+
if (styleOptions) {
|
|
48
|
+
widgetOptions[catName] = addClasses(widgetOptions[catName], styleOptions);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
flattenWidgetStyles(wstyles:css_fw.widgetstyles){
|
|
55
|
+
var flattened:any={};
|
|
56
|
+
let ignore=['__themes__'];
|
|
57
|
+
Object.keys(wstyles).forEach(wkey=>{
|
|
58
|
+
let wstyle=wstyles[wkey];
|
|
59
|
+
if(ignore.indexOf(wkey)>=0){
|
|
60
|
+
flattened[wkey]=wstyle;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if(_.isArray(wstyle)){
|
|
64
|
+
|
|
65
|
+
flattened[wkey]=wstyle.join(" ");
|
|
66
|
+
}
|
|
67
|
+
if(_.isObject(wstyle)){//is csscategories
|
|
68
|
+
flattened[wkey]=flattened[wkey]||{};
|
|
69
|
+
Object.keys(wstyle).forEach(catName=>{
|
|
70
|
+
let cssCat=wstyle[catName];
|
|
71
|
+
|
|
72
|
+
if(_.isArray(cssCat)){
|
|
73
|
+
flattened[wkey][catName]=cssCat.join(" ");
|
|
74
|
+
}else{
|
|
75
|
+
flattened[wkey][catName]=cssCat;
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
if(_.isString(wstyle)){
|
|
80
|
+
flattened[wkey]=wstyle;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
return flattened
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
defaultStyling:css_fw.widgetstyles={
|
|
87
|
+
array:{},
|
|
88
|
+
default:{fieldHtmlClass: "cssfw-form-control"},
|
|
89
|
+
__themes__:[{name:'notheme',text:'None'}],
|
|
90
|
+
__remove_item__:"cssfw-remove-item",
|
|
91
|
+
__array_item_nonref__:{
|
|
92
|
+
"htmlClass": "cssfw-array-item-nonref"
|
|
93
|
+
},
|
|
94
|
+
__active__:{activeClass:"cssfw-active"},
|
|
95
|
+
__array__:{htmlClass:"cssfw-array"},
|
|
96
|
+
__control_label__:{labelHtmlClass:"cssfw-control-label"},
|
|
97
|
+
__form_group__:{htmlClass: "cssfw-form-group"},
|
|
98
|
+
__field_addon_left__:"cssfw-addon-left",
|
|
99
|
+
__field_addon_right__:"cssfw-addon-right",
|
|
100
|
+
__help_block__:"cssfw-help-block",
|
|
101
|
+
__required_asterisk__:"cssfw-required-astersisk",
|
|
102
|
+
__screen_reader__: "cssfw-screen-reader"
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
theme:string
|
|
110
|
+
frameworkThemeSubs:Subscription;
|
|
111
|
+
constructor(
|
|
112
|
+
public changeDetector: ChangeDetectorRef,
|
|
113
|
+
public jsf: JsonSchemaFormService,
|
|
114
|
+
public jsfFLService:FrameworkLibraryService,
|
|
115
|
+
public cssFWService:CssframeworkService
|
|
116
|
+
/*@Inject(CSS_FRAMEWORK_CFG ) fwcfg: css_fw.frameworkcfg*/
|
|
117
|
+
|
|
118
|
+
) {
|
|
119
|
+
|
|
120
|
+
let activeFramework:any=this.jsfFLService.activeFramework;
|
|
121
|
+
let fwcfg=activeFramework.config||{};
|
|
122
|
+
this.widgetStyles = Object.assign(this.defaultStyling,fwcfg.widgetstyles);
|
|
123
|
+
let defaultTheme=this.widgetStyles.__themes__[0];
|
|
124
|
+
let defaultThemeName=cssFWService.activeRequestedTheme||defaultTheme.name;
|
|
125
|
+
this.theme=this.options?.theme|| defaultThemeName;
|
|
126
|
+
this.frameworkThemeSubs=cssFWService.frameworkTheme$.subscribe(newTheme=>{
|
|
127
|
+
this.theme=newTheme;
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
ngOnDestroy(): void {
|
|
133
|
+
this.frameworkThemeSubs.unsubscribe();
|
|
134
|
+
this.frameworkThemeSubs=null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
get showRemoveButton(): boolean {
|
|
139
|
+
if (!this.options.removable || this.options.readonly ||
|
|
140
|
+
this.layoutNode.type === '$ref'
|
|
141
|
+
) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if (this.layoutNode.recursiveReference) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (!this.layoutNode.arrayItem || !this.parentArray) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
// If array length <= minItems, don't allow removing any items
|
|
151
|
+
return this.parentArray.items.length - 1 <= this.parentArray.options.minItems ? false :
|
|
152
|
+
// For removable list items, allow removing any item
|
|
153
|
+
this.layoutNode.arrayItemType === 'list' ? true :
|
|
154
|
+
// For removable tuple items, only allow removing last item in list
|
|
155
|
+
this.layoutIndex[this.layoutIndex.length - 1] === this.parentArray.items.length - 2;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ngOnInit() {
|
|
159
|
+
this.initializeFramework();
|
|
160
|
+
if (this.layoutNode.arrayItem && this.layoutNode.type !== '$ref') {
|
|
161
|
+
this.parentArray = this.jsf.getParentNode(this);
|
|
162
|
+
if (this.parentArray) {
|
|
163
|
+
this.isOrderable = this.layoutNode.arrayItemType === 'list' &&
|
|
164
|
+
!this.options.readonly && this.parentArray.options.orderable;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ngOnChanges() {
|
|
170
|
+
if (!this.frameworkInitialized) {
|
|
171
|
+
this.initializeFramework();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
initializeFramework() {
|
|
176
|
+
if (this.layoutNode) {
|
|
177
|
+
this.options = cloneDeep(this.layoutNode.options);
|
|
178
|
+
this.widgetLayoutNode = {
|
|
179
|
+
...this.layoutNode,
|
|
180
|
+
options: cloneDeep(this.layoutNode.options)
|
|
181
|
+
};
|
|
182
|
+
this.widgetOptions = this.widgetLayoutNode.options;
|
|
183
|
+
this.formControl = this.jsf.getFormControl(this);
|
|
184
|
+
|
|
185
|
+
this.options.isInputWidget = inArray(this.layoutNode.type, [
|
|
186
|
+
'button', 'checkbox', 'checkboxes-inline', 'checkboxes', 'color',
|
|
187
|
+
'date', 'datetime-local', 'datetime', 'email', 'file', 'hidden',
|
|
188
|
+
'image', 'integer', 'month', 'number', 'password', 'radio',
|
|
189
|
+
'radiobuttons', 'radios-inline', 'radios', 'range', 'reset', 'search',
|
|
190
|
+
'select', 'submit', 'tel', 'text', 'textarea', 'time', 'url', 'week'
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
this.options.title = this.setTitle();
|
|
194
|
+
|
|
195
|
+
this.options.htmlClass =
|
|
196
|
+
addClasses(this.options.htmlClass, 'schema-form-' + this.layoutNode.type);
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
if (this.layoutNode.type === 'array') {
|
|
200
|
+
this.options.htmlClass = addClasses(this.options.htmlClass, this.widgetStyles.__array__.htmlClass);
|
|
201
|
+
} else if (this.layoutNode.arrayItem && this.layoutNode.type !== '$ref') {
|
|
202
|
+
this.options.htmlClass = addClasses(this.options.htmlClass, this.widgetStyles.__array_item_nonref__.htmlClass);
|
|
203
|
+
} else {
|
|
204
|
+
this.options.htmlClass = addClasses(this.options.htmlClass, this.widgetStyles.__form_group__.htmlClass);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
this.options.htmlClass =
|
|
210
|
+
this.layoutNode.type === 'array' ?
|
|
211
|
+
addClasses(this.options.htmlClass, ['border','shadow-md','p-1']) :
|
|
212
|
+
this.layoutNode.arrayItem && this.layoutNode.type !== '$ref' ?
|
|
213
|
+
addClasses(this.options.htmlClass, ['border','shadow-md','p-1']) :
|
|
214
|
+
addClasses(this.options.htmlClass, 'mb-1');
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
this.options.htmlClass =
|
|
219
|
+
this.layoutNode.type === 'array' ?
|
|
220
|
+
addClasses(this.options.htmlClass, this.widgetStyles.array.htmlClass):
|
|
221
|
+
this.layoutNode.arrayItem && this.layoutNode.type !== '$ref' ?
|
|
222
|
+
addClasses(this.options.htmlClass, this.widgetStyles.__array_item_nonref__.htmlClass):
|
|
223
|
+
addClasses(this.options.htmlClass, this.widgetStyles.__form_group__.htmlClass);
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
this.widgetOptions.htmlClass = '';
|
|
227
|
+
this.options.labelHtmlClass =
|
|
228
|
+
addClasses(this.options.labelHtmlClass, this.widgetStyles.__control_label__.labelHtmlClass);
|
|
229
|
+
this.widgetOptions.activeClass =
|
|
230
|
+
addClasses(this.widgetOptions.activeClass, this.widgetStyles.__active__.activeClass);
|
|
231
|
+
this.options.fieldAddonLeft =
|
|
232
|
+
this.options.fieldAddonLeft || this.options.prepend;
|
|
233
|
+
this.options.fieldAddonRight =
|
|
234
|
+
this.options.fieldAddonRight || this.options.append;
|
|
235
|
+
|
|
236
|
+
// Add asterisk to titles if required
|
|
237
|
+
if (this.options.title && this.layoutNode.type !== 'tab' &&
|
|
238
|
+
!this.options.notitle && this.options.required &&
|
|
239
|
+
!this.options.title.includes('*')
|
|
240
|
+
) {
|
|
241
|
+
let required_asterisk_class=this.widgetStyles.__required_asterisk__||'text-danger'
|
|
242
|
+
this.options.title += ` <strong class="${required_asterisk_class}">*</strong>`;
|
|
243
|
+
}
|
|
244
|
+
if (this.layoutNode.type == 'optionfieldset') {
|
|
245
|
+
this.options.messageLocation = 'top';
|
|
246
|
+
}
|
|
247
|
+
// Set miscelaneous styles and settings for each control type
|
|
248
|
+
this.applyCssClasses(this.layoutNode.type, this.widgetOptions, this.options.style);
|
|
249
|
+
if (this.formControl) {
|
|
250
|
+
this.updateHelpBlock(this.formControl.status);
|
|
251
|
+
this.formControl.statusChanges.subscribe(status => this.updateHelpBlock(status));
|
|
252
|
+
|
|
253
|
+
if (this.options.debug) {
|
|
254
|
+
const vars: any[] = [];
|
|
255
|
+
this.debugOutput = map(vars, thisVar => JSON.stringify(thisVar, null, 2)).join('\n');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
this.frameworkInitialized = true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
updateHelpBlock(status) {
|
|
264
|
+
this.options.helpBlock = status === 'INVALID' &&
|
|
265
|
+
this.options.enableErrorState && this.formControl.errors &&
|
|
266
|
+
(this.formControl.dirty || this.options.feedbackOnRender) ?
|
|
267
|
+
this.jsf.formatErrors(this.formControl.errors, this.options.validationMessages) :
|
|
268
|
+
this.options.description || this.options.help || null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setTitle(): string {
|
|
272
|
+
switch (this.layoutNode.type) {
|
|
273
|
+
case 'button':
|
|
274
|
+
case 'checkbox':
|
|
275
|
+
case 'section':
|
|
276
|
+
case 'help':
|
|
277
|
+
case 'msg':
|
|
278
|
+
case 'submit':
|
|
279
|
+
case 'message':
|
|
280
|
+
case 'tabarray':
|
|
281
|
+
case 'tabs':
|
|
282
|
+
case '$ref':
|
|
283
|
+
return null;
|
|
284
|
+
case 'advancedfieldset':
|
|
285
|
+
this.widgetOptions.expandable = true;
|
|
286
|
+
this.widgetOptions.title = 'Advanced options';
|
|
287
|
+
return null;
|
|
288
|
+
case 'authfieldset':
|
|
289
|
+
this.widgetOptions.expandable = true;
|
|
290
|
+
this.widgetOptions.title = 'Authentication settings';
|
|
291
|
+
return null;
|
|
292
|
+
case 'fieldset':
|
|
293
|
+
this.widgetOptions.title = this.options.title;
|
|
294
|
+
return null;
|
|
295
|
+
default:
|
|
296
|
+
this.widgetOptions.title = null;
|
|
297
|
+
return this.jsf.setItemTitle(this);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
removeItem() {
|
|
302
|
+
this.jsf.removeItem(this);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { InjectionToken } from "@angular/core";
|
|
2
|
+
|
|
3
|
+
export const CSS_FRAMEWORK_CFG = new InjectionToken<css_fw.frameworkcfg>('CSS_FRAMEWORK_CFG');
|
|
4
|
+
|
|
5
|
+
export namespace css_fw{
|
|
6
|
+
|
|
7
|
+
export type themeKV=
|
|
8
|
+
{
|
|
9
|
+
name:string,
|
|
10
|
+
text:string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class csscategories{
|
|
14
|
+
fieldHtmlClass?:string|string[]
|
|
15
|
+
labelHtmlClass?:string|string[]
|
|
16
|
+
htmlClass?:string|string[]
|
|
17
|
+
itemLabelHtmlClass?:string|string[]
|
|
18
|
+
activeClass?:string|string[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type widgetstyles={
|
|
22
|
+
'$ref'?:csscategories,
|
|
23
|
+
'alt-date'?:csscategories,
|
|
24
|
+
'alt-datetime'?:csscategories,
|
|
25
|
+
__themes__?:themeKV[],
|
|
26
|
+
__array_item_nonref__:csscategories,
|
|
27
|
+
__form_group__:csscategories,
|
|
28
|
+
__control_label__:csscategories,
|
|
29
|
+
__active__:csscategories,
|
|
30
|
+
__required_asterisk__:string,
|
|
31
|
+
__array__:csscategories,
|
|
32
|
+
'__remove_item__':string,
|
|
33
|
+
__help_block__:string,
|
|
34
|
+
__field_addon_left__:string
|
|
35
|
+
__field_addon_right__:string,
|
|
36
|
+
__screen_reader__:string,
|
|
37
|
+
array:csscategories,
|
|
38
|
+
authfieldset?:csscategories,
|
|
39
|
+
advancedfieldset?:csscategories,
|
|
40
|
+
button?:csscategories,
|
|
41
|
+
checkbox?:csscategories,
|
|
42
|
+
checkboxes?:csscategories,
|
|
43
|
+
checkboxbuttons?:csscategories,
|
|
44
|
+
'checkboxes-inline'?:csscategories,
|
|
45
|
+
date?:csscategories,
|
|
46
|
+
'datetime-local'?:csscategories,
|
|
47
|
+
fieldset?:csscategories,
|
|
48
|
+
integer?:csscategories,
|
|
49
|
+
number?:csscategories,
|
|
50
|
+
optionfieldset?:csscategories,
|
|
51
|
+
password?:csscategories,
|
|
52
|
+
radiobuttons?:csscategories,
|
|
53
|
+
radio?:csscategories,
|
|
54
|
+
radios?:csscategories,
|
|
55
|
+
"radios-inline"?:csscategories,
|
|
56
|
+
'range'?:csscategories,
|
|
57
|
+
section?:csscategories,
|
|
58
|
+
selectfieldset?:csscategories,
|
|
59
|
+
select?:csscategories,
|
|
60
|
+
submit?:csscategories,
|
|
61
|
+
text?:csscategories,
|
|
62
|
+
tabs?:csscategories,
|
|
63
|
+
tabarray?:csscategories,
|
|
64
|
+
textarea?:csscategories,
|
|
65
|
+
default:csscategories
|
|
66
|
+
}
|
|
67
|
+
export type frameworkcfg={
|
|
68
|
+
name:string,
|
|
69
|
+
text:string;
|
|
70
|
+
stylesheets:string[],
|
|
71
|
+
scripts?:string[],
|
|
72
|
+
widgetstyles:widgetstyles,
|
|
73
|
+
widgets?:any;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { NgModule } from '@angular/core';
|
|
3
|
+
import { FrameworkLibraryService, JsonSchemaFormModule, JsonSchemaFormService, WidgetLibraryModule, WidgetLibraryService } from '@ng-formworks/core';
|
|
4
|
+
import { CssFrameworkComponent } from './css-framework.component';
|
|
5
|
+
import { CssframeworkService } from './css-framework.service';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@NgModule({
|
|
10
|
+
declarations: [
|
|
11
|
+
CssFrameworkComponent
|
|
12
|
+
],
|
|
13
|
+
imports: [
|
|
14
|
+
JsonSchemaFormModule,
|
|
15
|
+
CommonModule,
|
|
16
|
+
WidgetLibraryModule,
|
|
17
|
+
],
|
|
18
|
+
exports: [
|
|
19
|
+
CssFrameworkComponent
|
|
20
|
+
],
|
|
21
|
+
providers: [
|
|
22
|
+
JsonSchemaFormService,
|
|
23
|
+
FrameworkLibraryService,
|
|
24
|
+
WidgetLibraryService,
|
|
25
|
+
CssframeworkService
|
|
26
|
+
// { provide: Framework, useClass: CssFramework, multi: true },
|
|
27
|
+
]
|
|
28
|
+
})
|
|
29
|
+
export class CssFrameworkModule { }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CssframeworkService } from './css-framework.service';
|
|
4
|
+
|
|
5
|
+
describe('CssframeworkService', () => {
|
|
6
|
+
let service: CssframeworkService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
TestBed.configureTestingModule({});
|
|
10
|
+
service = TestBed.inject(CssframeworkService);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
expect(service).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Observable, Subject } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
@Injectable({
|
|
5
|
+
providedIn: 'root'
|
|
6
|
+
})
|
|
7
|
+
export class CssframeworkService {
|
|
8
|
+
|
|
9
|
+
frameworkTheme$: Observable<string>;
|
|
10
|
+
activeRequestedTheme:string;
|
|
11
|
+
frameworkThemeSubject: Subject<string>;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.frameworkThemeSubject = new Subject<string>();
|
|
14
|
+
this.frameworkTheme$ = this.frameworkThemeSubject.asObservable();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//TODO-review: this acts as a public api to change the theme
|
|
18
|
+
//but doesn't do the actual change, instead it relies on
|
|
19
|
+
//the CssFramewkCoromponent having subscribed to listen
|
|
20
|
+
//and perform the actual theme change
|
|
21
|
+
requestThemeChange(themeName:string){
|
|
22
|
+
this.frameworkThemeSubject.next(themeName);
|
|
23
|
+
this.activeRequestedTheme=themeName;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//TODO-review:there's no way of knowing what the individual component instance
|
|
27
|
+
//has set its theme to, this is just the theme made through the requestThemeChange
|
|
28
|
+
//calls and not guaranteed to correspond to the actual theme set by the
|
|
29
|
+
//component instance themselves
|
|
30
|
+
getActiveRequestedTheme():string{
|
|
31
|
+
return this.activeRequestedTheme;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Inject, Injectable } from '@angular/core';
|
|
2
|
+
import { Framework } from '@ng-formworks/core';
|
|
3
|
+
import { CssFrameworkComponent } from './css-framework.component';
|
|
4
|
+
import { CSS_FRAMEWORK_CFG, css_fw } from './css-framework.defs';
|
|
5
|
+
import { CssframeworkService } from './css-framework.service';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class CssFramework extends Framework {
|
|
11
|
+
name = 'css';
|
|
12
|
+
|
|
13
|
+
framework:any = CssFrameworkComponent;
|
|
14
|
+
config:css_fw.frameworkcfg
|
|
15
|
+
constructor(@Inject(CSS_FRAMEWORK_CFG ) cfg:css_fw.frameworkcfg,public cssFWService:CssframeworkService){
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
this.name=cfg.name;
|
|
19
|
+
this.text=cfg.text||this.name;
|
|
20
|
+
this.stylesheets=cfg.stylesheets;
|
|
21
|
+
this.scripts=cfg.scripts;
|
|
22
|
+
this.config=cfg;
|
|
23
|
+
this.widgets=cfg.widgets;
|
|
24
|
+
}
|
|
25
|
+
getActiveTheme():css_fw.themeKV{
|
|
26
|
+
let activeRequestedThemeName=this.cssFWService.getActiveRequestedTheme();
|
|
27
|
+
let frameWorkThemes=this.config?.widgetstyles?.__themes__;
|
|
28
|
+
|
|
29
|
+
let theme=frameWorkThemes && frameWorkThemes[0]
|
|
30
|
+
if(activeRequestedThemeName){//if not set return first theme in config;
|
|
31
|
+
theme={name:activeRequestedThemeName,text:activeRequestedThemeName};
|
|
32
|
+
if(frameWorkThemes){
|
|
33
|
+
let filtered=frameWorkThemes.filter(theme=>{return theme.name==activeRequestedThemeName});
|
|
34
|
+
theme=(filtered && filtered[0])||theme;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return theme;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
requestThemeChange(name:string){
|
|
41
|
+
this.cssFWService.requestThemeChange(name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
registerTheme(newTheme:css_fw.themeKV,overwrite:boolean=true):boolean{
|
|
45
|
+
let themeList=this.config?.widgetstyles?.__themes__||[];
|
|
46
|
+
let matchedThemes=themeList.filter(theme=>{return newTheme.name==theme.name});
|
|
47
|
+
if(matchedThemes && matchedThemes[0]){
|
|
48
|
+
if(overwrite){
|
|
49
|
+
matchedThemes[0].text=newTheme.text;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if(!overwrite){
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
if(!matchedThemes || matchedThemes.length==0){
|
|
58
|
+
let cfg:any=this.config
|
|
59
|
+
cfg.widgetstyles= this.config.widgetstyles||{};
|
|
60
|
+
cfg.widgetstyles.__themes__=cfg.widgetstyles.__themes__||[];
|
|
61
|
+
cfg.widgetstyles.__themes__.push(newTheme);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
unregisterTheme(name:string):boolean{
|
|
67
|
+
let themeList=this.config?.widgetstyles?.__themes__;
|
|
68
|
+
let foundInd=-1;
|
|
69
|
+
if(themeList){
|
|
70
|
+
themeList.forEach((theme,ind)=>{
|
|
71
|
+
if(name==theme.name){
|
|
72
|
+
foundInd=ind;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if(foundInd>=0){
|
|
76
|
+
themeList.splice(foundInd,1);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getConfig():css_fw.frameworkcfg{
|
|
84
|
+
return this.config;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
stylesheets = [
|
|
89
|
+
//TODO-enable for dev only
|
|
90
|
+
cdn.tailwindcss.com/3.3.3'
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
scripts = [
|
|
95
|
+
|
|
96
|
+
];
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public API Surface of ng-formworks-cssframework
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './lib/css-framework.component';
|
|
6
|
+
export * from './lib/css-framework.defs';
|
|
7
|
+
export * from './lib/css-framework.module';
|
|
8
|
+
export * from './lib/css-framework.service';
|
|
9
|
+
export * from './lib/css.framework';
|
|
10
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/lib",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"inlineSources": true,
|
|
9
|
+
"types": []
|
|
10
|
+
},
|
|
11
|
+
"exclude": [
|
|
12
|
+
"**/*.spec.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/spec",
|
|
6
|
+
"types": [
|
|
7
|
+
"jasmine"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"**/*.spec.ts",
|
|
12
|
+
"**/*.d.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|