@node-projects/web-component-designer 0.0.167 → 0.0.169
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/dist/elements/documentContainer.d.ts +7 -0
- package/dist/elements/documentContainer.js +23 -0
- package/dist/elements/helper/CssUnitConverter.js +11 -1
- package/dist/elements/helper/contextMenu/ContextMenu.js +21 -21
- package/dist/elements/item/DesignItem.d.ts +4 -1
- package/dist/elements/item/DesignItem.js +28 -1
- package/dist/elements/item/IDesignItem.d.ts +4 -1
- package/dist/elements/services/BaseServiceContainer.d.ts +4 -0
- package/dist/elements/services/BaseServiceContainer.js +4 -0
- package/dist/elements/services/DefaultServiceBootstrap.js +9 -2
- package/dist/elements/services/InstanceServiceContainer.d.ts +3 -0
- package/dist/elements/services/InstanceServiceContainer.js +3 -0
- package/dist/elements/services/ServiceContainer.d.ts +12 -3
- package/dist/elements/services/elementsService/IElementDefinition.d.ts +0 -3
- package/dist/elements/services/instanceService/DefaultInstanceService.js +0 -12
- package/dist/elements/services/propertiesService/IPropertiesService.d.ts +2 -1
- package/dist/elements/services/propertiesService/IProperty copy.d.ts +22 -0
- package/dist/elements/services/propertiesService/IProperty copy.js +1 -0
- package/dist/elements/services/propertiesService/IPropertyGroup.d.ts +6 -0
- package/dist/elements/services/propertiesService/IPropertyGroup.js +1 -0
- package/dist/elements/services/propertiesService/IPropertyTabsService.d.ts +8 -0
- package/dist/elements/services/propertiesService/IPropertyTabsService.js +1 -0
- package/dist/elements/services/propertiesService/PropertyGroupsService.d.ts +2 -2
- package/dist/elements/services/propertiesService/PropertyTabsService.d.ts +17 -0
- package/dist/elements/services/propertiesService/PropertyTabsService.js +29 -0
- package/dist/elements/services/propertiesService/services/AbstractPropertiesService.d.ts +2 -1
- package/dist/elements/services/propertiesService/services/AbstractPropertiesService.js +7 -7
- package/dist/elements/services/propertiesService/services/CommonPropertiesService.d.ts +2 -1
- package/dist/elements/services/propertiesService/services/CssPropertiesService.d.ts +2 -1
- package/dist/elements/services/propertiesService/services/CssPropertiesService.js +8 -2
- package/dist/elements/services/stylesheetService/CssTreeStylesheetService.d.ts +47 -0
- package/dist/elements/services/stylesheetService/CssTreeStylesheetService.js +183 -0
- package/dist/elements/services/stylesheetService/IStylesheetService.d.ts +29 -0
- package/dist/elements/services/stylesheetService/IStylesheetService.js +1 -0
- package/dist/elements/services/stylesheetService/SpecificityCalculator.d.ts +7 -0
- package/dist/elements/services/stylesheetService/SpecificityCalculator.js +178 -0
- package/dist/elements/services/stylesheetService/StylesheetService.d.ts +28 -0
- package/dist/elements/services/stylesheetService/StylesheetService.js +108 -0
- package/dist/elements/widgets/designerView/designerCanvas.d.ts +6 -0
- package/dist/elements/widgets/designerView/designerCanvas.js +73 -34
- package/dist/elements/widgets/propertyGrid/PropertyGridPropertyList.d.ts +2 -0
- package/dist/elements/widgets/propertyGrid/PropertyGridPropertyList.js +96 -66
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/package.json +6 -3
|
@@ -3,6 +3,7 @@ import { IDesignItem } from '../../../item/IDesignItem.js';
|
|
|
3
3
|
import { BindingTarget } from '../../../item/BindingTarget.js';
|
|
4
4
|
import { CommonPropertiesService } from './CommonPropertiesService.js';
|
|
5
5
|
import { RefreshMode } from '../IPropertiesService.js';
|
|
6
|
+
import { IPropertyGroup } from '../IPropertyGroup.js';
|
|
6
7
|
export declare class CssPropertiesService extends CommonPropertiesService {
|
|
7
8
|
getRefreshMode(designItem: IDesignItem): RefreshMode.none | RefreshMode.fullOnValueChange;
|
|
8
9
|
layout: IProperty[];
|
|
@@ -11,7 +12,7 @@ export declare class CssPropertiesService extends CommonPropertiesService {
|
|
|
11
12
|
constructor(name: 'styles' | 'layout' | 'grid' | 'flex');
|
|
12
13
|
isHandledElement(designItem: IDesignItem): boolean;
|
|
13
14
|
getProperty(designItem: IDesignItem, name: string): IProperty;
|
|
14
|
-
getProperties(designItem: IDesignItem): IProperty[];
|
|
15
|
+
getProperties(designItem: IDesignItem): IProperty[] | IPropertyGroup[];
|
|
15
16
|
getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget;
|
|
16
17
|
setValue(designItems: IDesignItem[], property: IProperty, value: any): void;
|
|
17
18
|
}
|
|
@@ -195,8 +195,14 @@ export class CssPropertiesService extends CommonPropertiesService {
|
|
|
195
195
|
if (this.name == 'styles') {
|
|
196
196
|
if (!designItem)
|
|
197
197
|
return [];
|
|
198
|
-
let
|
|
199
|
-
arr.
|
|
198
|
+
let styles = designItem.getAllStyles();
|
|
199
|
+
let arr = styles.map(x => ({ name: x.selector ?? '<local>', description: x.stylesheetName ?? '', properties: [
|
|
200
|
+
...x.declarations.map(y => ({ name: y.name, renamable: true, type: 'string', service: this, propertyType: PropertyType.cssValue })),
|
|
201
|
+
{ name: '', type: 'addNew', service: this, propertyType: PropertyType.complex }
|
|
202
|
+
]
|
|
203
|
+
}));
|
|
204
|
+
//let arr: IProperty[] = Array.from(designItem.styles(), ([key, value]) => ({ name: key, renamable: true, type: 'string', service: this, propertyType: PropertyType.cssValue }));
|
|
205
|
+
//arr.push({ name: '', type: 'addNew', service: this, propertyType: PropertyType.complex });
|
|
200
206
|
return arr;
|
|
201
207
|
}
|
|
202
208
|
return this[this.name];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IDesignItem } from "../../item/IDesignItem.js";
|
|
2
|
+
import { IProperty } from "../propertiesService/IProperty.js";
|
|
3
|
+
import { IStyleDeclaration, IStyleRule, IStylesheet, IStylesheetService } from "./IStylesheetService.js";
|
|
4
|
+
import type * as csstree from 'css-tree';
|
|
5
|
+
import { TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
csstree: {
|
|
9
|
+
fromPlainObject(node: csstree.CssNodePlain): csstree.CssNode;
|
|
10
|
+
toPlainObject(node: csstree.CssNode): csstree.CssNodePlain;
|
|
11
|
+
parse(text: string, options?: csstree.ParseOptions): csstree.CssNode;
|
|
12
|
+
generate(ast: csstree.CssNode, options?: csstree.GenerateOptions): string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
interface IRuleWithAST extends IStyleRule {
|
|
17
|
+
ast: csstree.RulePlain;
|
|
18
|
+
declarations: IDeclarationWithAST[];
|
|
19
|
+
}
|
|
20
|
+
interface IDeclarationWithAST extends IStyleDeclaration {
|
|
21
|
+
ast: csstree.DeclarationPlain;
|
|
22
|
+
parent: IStyleRule;
|
|
23
|
+
}
|
|
24
|
+
export declare class CssTreeStylesheetService implements IStylesheetService {
|
|
25
|
+
private _stylesheets;
|
|
26
|
+
stylesheetChanged: TypedEvent<{
|
|
27
|
+
stylesheet: IStylesheet;
|
|
28
|
+
}>;
|
|
29
|
+
constructor();
|
|
30
|
+
setStylesheets(stylesheets: IStylesheet[]): void;
|
|
31
|
+
getStylesheets(): IStylesheet[];
|
|
32
|
+
private getAppliedRulesInternal;
|
|
33
|
+
getAppliedRules(designItem: IDesignItem): IRuleWithAST[];
|
|
34
|
+
private getDeclarationInternal;
|
|
35
|
+
getDeclarations(designItem: IDesignItem, prop: IProperty): IDeclarationWithAST[];
|
|
36
|
+
updateDeclarationWithProperty(designItem: IDesignItem, property: IProperty, value: string, important: boolean): boolean;
|
|
37
|
+
updateDeclarationWithDeclaration(declaration: IDeclarationWithAST, value: string, important: boolean): boolean;
|
|
38
|
+
private rulesFromAST;
|
|
39
|
+
private astHasChildren;
|
|
40
|
+
private buildSelectorString;
|
|
41
|
+
private getSpecificity;
|
|
42
|
+
private findDeclarationInRule;
|
|
43
|
+
private elementMatchesASelector;
|
|
44
|
+
private buildAtRuleString;
|
|
45
|
+
private sortDeclarations;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
2
|
+
import { calculate as calculateSpecifity } from "./SpecificityCalculator.js";
|
|
3
|
+
export class CssTreeStylesheetService {
|
|
4
|
+
_stylesheets = new Map();
|
|
5
|
+
stylesheetChanged = new TypedEvent();
|
|
6
|
+
constructor() { }
|
|
7
|
+
setStylesheets(stylesheets) {
|
|
8
|
+
if (stylesheets != null) {
|
|
9
|
+
this._stylesheets = new Map();
|
|
10
|
+
for (let stylesheet of stylesheets) {
|
|
11
|
+
this._stylesheets.set(stylesheet.name, {
|
|
12
|
+
stylesheet: stylesheet,
|
|
13
|
+
ast: window.csstree.toPlainObject((window.csstree.parse(stylesheet.stylesheet, { positions: true, parseValue: false })))
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
this._stylesheets = null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
getStylesheets() {
|
|
22
|
+
let stylesheets = [];
|
|
23
|
+
for (let item of this._stylesheets) {
|
|
24
|
+
stylesheets.push(item[1].stylesheet);
|
|
25
|
+
}
|
|
26
|
+
;
|
|
27
|
+
return stylesheets;
|
|
28
|
+
}
|
|
29
|
+
/* Section covers the retrieval of rules and declarations */
|
|
30
|
+
getAppliedRulesInternal(designItem) {
|
|
31
|
+
let styles = [];
|
|
32
|
+
for (let item of this._stylesheets) {
|
|
33
|
+
if (!item[1].ast || !this.astHasChildren(item[1].ast))
|
|
34
|
+
break;
|
|
35
|
+
styles = styles.concat(Array.from(this.rulesFromAST(item[1].ast, item[1].stylesheet.stylesheet, item[0], designItem)));
|
|
36
|
+
}
|
|
37
|
+
;
|
|
38
|
+
return styles;
|
|
39
|
+
}
|
|
40
|
+
getAppliedRules(designItem) {
|
|
41
|
+
let rules = this.getAppliedRulesInternal(designItem);
|
|
42
|
+
if (!rules || rules.length == 0)
|
|
43
|
+
return [];
|
|
44
|
+
let retCollection = [];
|
|
45
|
+
for (let rule of rules) {
|
|
46
|
+
retCollection.push({
|
|
47
|
+
...rule,
|
|
48
|
+
declarations: rule.ast.block.children.map((declaration) => {
|
|
49
|
+
return {
|
|
50
|
+
name: declaration.property,
|
|
51
|
+
value: declaration.value.value,
|
|
52
|
+
important: declaration.important == true,
|
|
53
|
+
specificity: rule.specificity,
|
|
54
|
+
parent: rule,
|
|
55
|
+
ast: declaration,
|
|
56
|
+
};
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return retCollection;
|
|
61
|
+
}
|
|
62
|
+
getDeclarationInternal(designItem, prop) {
|
|
63
|
+
let rules = this.getAppliedRulesInternal(designItem);
|
|
64
|
+
if (!rules)
|
|
65
|
+
return null;
|
|
66
|
+
let declarations = [];
|
|
67
|
+
for (let rule of rules) {
|
|
68
|
+
let declaration = this.findDeclarationInRule(rule.ast, prop);
|
|
69
|
+
if (!declaration)
|
|
70
|
+
continue;
|
|
71
|
+
declarations.push({
|
|
72
|
+
ast: declaration,
|
|
73
|
+
parent: rule,
|
|
74
|
+
name: prop.name,
|
|
75
|
+
value: declaration.value.value,
|
|
76
|
+
important: declaration.important == true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
;
|
|
80
|
+
return declarations;
|
|
81
|
+
}
|
|
82
|
+
getDeclarations(designItem, prop) {
|
|
83
|
+
let declarations = this.getDeclarationInternal(designItem, prop);
|
|
84
|
+
if (!declarations)
|
|
85
|
+
return null;
|
|
86
|
+
return this.sortDeclarations(declarations);
|
|
87
|
+
}
|
|
88
|
+
/* Section covers the update of rules and declarations */
|
|
89
|
+
updateDeclarationWithProperty(designItem, property, value, important) {
|
|
90
|
+
let sortedDecl = this.sortDeclarations(this.getDeclarationInternal(designItem, property));
|
|
91
|
+
if (!sortedDecl) {
|
|
92
|
+
// no declaration of property yet
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
updateDeclarationWithDeclaration(declaration, value, important) {
|
|
98
|
+
declaration.ast.value = window.csstree.toPlainObject(window.csstree.parse(declaration.name + ": " + value, { context: 'declaration', parseValue: false })).value;
|
|
99
|
+
this._stylesheets.get(declaration.parent.stylesheetName).stylesheet.stylesheet = window.csstree.generate(window.csstree.fromPlainObject(this._stylesheets.get(declaration.parent.stylesheetName).ast));
|
|
100
|
+
this.stylesheetChanged.emit({ stylesheet: this._stylesheets.get(declaration.parent.stylesheetName).stylesheet });
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/* Section covers the internal traversal creation of rules and declarations */
|
|
104
|
+
*rulesFromAST(ast, stylesheet, source, designItem, previousCheck = '') {
|
|
105
|
+
let parent = ast["children"] != null ? ast : ast.block;
|
|
106
|
+
for (const child of parent.children) {
|
|
107
|
+
if (child.type == "Atrule") {
|
|
108
|
+
const currentCheck = this.buildAtRuleString(child, stylesheet);
|
|
109
|
+
if (currentCheck.type === "@media" && !window.matchMedia(currentCheck.sel))
|
|
110
|
+
continue;
|
|
111
|
+
if (currentCheck.type === "@supports" && !CSS.supports(currentCheck.sel))
|
|
112
|
+
continue;
|
|
113
|
+
let ruleCollection = this.rulesFromAST(child, stylesheet, source, designItem, previousCheck + currentCheck.type + " " + currentCheck.sel + "\n");
|
|
114
|
+
if (ruleCollection) {
|
|
115
|
+
for (const r of ruleCollection) {
|
|
116
|
+
if (!this.elementMatchesASelector(designItem, this.buildSelectorString(r.ast.prelude)))
|
|
117
|
+
continue;
|
|
118
|
+
yield r;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (child.type == "Rule") {
|
|
123
|
+
let selectors = this.buildSelectorString(child.prelude);
|
|
124
|
+
if (!this.elementMatchesASelector(designItem, selectors))
|
|
125
|
+
continue;
|
|
126
|
+
yield ({
|
|
127
|
+
ast: child,
|
|
128
|
+
selector: previousCheck + this.buildSelectorString(child.prelude).join(", "),
|
|
129
|
+
specificity: this.getSpecificity(child.prelude),
|
|
130
|
+
stylesheetName: source,
|
|
131
|
+
declarations: null,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
;
|
|
136
|
+
}
|
|
137
|
+
/* Utility functions for building selectors, specificity and so on */
|
|
138
|
+
astHasChildren(ast) {
|
|
139
|
+
return ast != null && ast["children"] != null && ast["children"].length > 0;
|
|
140
|
+
}
|
|
141
|
+
buildSelectorString(selectorsAST) {
|
|
142
|
+
let selectors = [];
|
|
143
|
+
for (let selector of selectorsAST.children) {
|
|
144
|
+
let sel = "";
|
|
145
|
+
for (let fraction of selector.children) {
|
|
146
|
+
sel += fraction.name;
|
|
147
|
+
}
|
|
148
|
+
selectors.push(sel);
|
|
149
|
+
}
|
|
150
|
+
;
|
|
151
|
+
return selectors;
|
|
152
|
+
}
|
|
153
|
+
getSpecificity(selector) {
|
|
154
|
+
const specificities = calculateSpecifity(selector);
|
|
155
|
+
let sum = 0;
|
|
156
|
+
specificities.forEach(specificity => sum += specificity.a * 10000 + specificity.b * 100 + specificity.c);
|
|
157
|
+
return sum;
|
|
158
|
+
}
|
|
159
|
+
findDeclarationInRule(rule, property) {
|
|
160
|
+
return rule.block.children.find(declaration => declaration.property == property.name);
|
|
161
|
+
}
|
|
162
|
+
elementMatchesASelector(designItem, selectors) {
|
|
163
|
+
for (const selector of selectors)
|
|
164
|
+
if (designItem.element.matches(selector))
|
|
165
|
+
return true;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
buildAtRuleString(ast, stylesheet) {
|
|
169
|
+
return {
|
|
170
|
+
sel: stylesheet.slice(ast.prelude.loc.start.offset, ast.prelude.loc.end.offset),
|
|
171
|
+
type: "@" + ast.name
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
sortDeclarations(declarations) {
|
|
175
|
+
if (declarations == null || declarations.length == 0)
|
|
176
|
+
return null;
|
|
177
|
+
return declarations.sort((dec1, dec2) => {
|
|
178
|
+
if (dec1.parent.specificity > dec2.parent.specificity)
|
|
179
|
+
return -1;
|
|
180
|
+
return 1;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
2
|
+
import { IDesignItem } from "../../item/IDesignItem.js";
|
|
3
|
+
import { IProperty } from "../propertiesService/IProperty.js";
|
|
4
|
+
export interface IStyleRule {
|
|
5
|
+
selector: string;
|
|
6
|
+
declarations: IStyleDeclaration[];
|
|
7
|
+
specificity: number;
|
|
8
|
+
stylesheetName?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface IStyleDeclaration {
|
|
11
|
+
name: string;
|
|
12
|
+
value: string;
|
|
13
|
+
important: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface IStylesheet {
|
|
16
|
+
stylesheet: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
export interface IStylesheetService {
|
|
20
|
+
setStylesheets(stylesheets: IStylesheet[]): any;
|
|
21
|
+
getStylesheets(): IStylesheet[];
|
|
22
|
+
getAppliedRules(designItem: IDesignItem): IStyleRule[];
|
|
23
|
+
getDeclarations(designItem: IDesignItem, property: IProperty): IStyleDeclaration[];
|
|
24
|
+
updateDeclarationWithProperty(designItem: IDesignItem, property: IProperty, value: string, important: boolean): boolean;
|
|
25
|
+
updateDeclarationWithDeclaration(declaration: IStyleDeclaration, value: string, important: boolean): boolean;
|
|
26
|
+
stylesheetChanged: TypedEvent<{
|
|
27
|
+
stylesheet: IStylesheet;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const calculateSpecificityOfSelectorObject = (selectorObj) => {
|
|
2
|
+
// https://www.w3.org/TR/selectors-4/#specificity-rules
|
|
3
|
+
const specificity = { a: 0, b: 0, c: 0 };
|
|
4
|
+
selectorObj.children.forEach((child) => {
|
|
5
|
+
switch (child.type) {
|
|
6
|
+
case 'IdSelector':
|
|
7
|
+
specificity.a += 1;
|
|
8
|
+
break;
|
|
9
|
+
case 'AttributeSelector':
|
|
10
|
+
case 'ClassSelector':
|
|
11
|
+
specificity.b += 1;
|
|
12
|
+
break;
|
|
13
|
+
case 'PseudoClassSelector':
|
|
14
|
+
switch (child.name.toLowerCase()) {
|
|
15
|
+
// “The specificity of a :where() pseudo-class is replaced by zero.”
|
|
16
|
+
case 'where':
|
|
17
|
+
// Noop :)
|
|
18
|
+
break;
|
|
19
|
+
// “The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the specificity of the most specific complex selector in its selector list argument.“
|
|
20
|
+
case 'is':
|
|
21
|
+
case 'matches':
|
|
22
|
+
case '-webkit-any':
|
|
23
|
+
case '-moz-any':
|
|
24
|
+
case 'any':
|
|
25
|
+
case 'not':
|
|
26
|
+
case 'has':
|
|
27
|
+
if (child.children) {
|
|
28
|
+
// Calculate Specificity from nested SelectorList
|
|
29
|
+
const max1 = max(...calculate(child.children.first));
|
|
30
|
+
// Adjust orig specificity
|
|
31
|
+
specificity.a += max1.a;
|
|
32
|
+
specificity.b += max1.b;
|
|
33
|
+
specificity.c += max1.c;
|
|
34
|
+
}
|
|
35
|
+
break;
|
|
36
|
+
// “The specificity of an :nth-child() or :nth-last-child() selector is the specificity of the pseudo class itself (counting as one pseudo-class selector) plus the specificity of the most specific complex selector in its selector list argument”
|
|
37
|
+
case 'nth-child':
|
|
38
|
+
case 'nth-last-child':
|
|
39
|
+
specificity.b += 1;
|
|
40
|
+
if (child.children.first.selector) {
|
|
41
|
+
// Calculate Specificity from SelectorList
|
|
42
|
+
const max2 = max(...calculate(child.children.first.selector));
|
|
43
|
+
// Adjust orig specificity
|
|
44
|
+
specificity.a += max2.a;
|
|
45
|
+
specificity.b += max2.b;
|
|
46
|
+
specificity.c += max2.c;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
// “The specificity of :host is that of a pseudo-class. The specificity of :host() is that of a pseudo-class, plus the specificity of its argument.”
|
|
50
|
+
// “The specificity of :host-context() is that of a pseudo-class, plus the specificity of its argument.”
|
|
51
|
+
case 'host-context':
|
|
52
|
+
case 'host':
|
|
53
|
+
specificity.b += 1;
|
|
54
|
+
if (child.children) {
|
|
55
|
+
// Workaround to a css-tree bug in which it allows complex selectors instead of only compound selectors
|
|
56
|
+
// We work around it by filtering out any Combinator and successive Selectors
|
|
57
|
+
const childAST = { type: 'Selector', children: [] };
|
|
58
|
+
let foundCombinator = false;
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
child.children.first.children.forEach((entry) => {
|
|
61
|
+
if (foundCombinator)
|
|
62
|
+
return false;
|
|
63
|
+
if (entry.type === 'Combinator') {
|
|
64
|
+
foundCombinator = true;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
childAST.children.push(entry);
|
|
68
|
+
});
|
|
69
|
+
// Calculate Specificity from Selector
|
|
70
|
+
const childSpecificity = calculate(childAST)[0];
|
|
71
|
+
// Adjust orig specificity
|
|
72
|
+
specificity.a += childSpecificity.a;
|
|
73
|
+
specificity.b += childSpecificity.b;
|
|
74
|
+
specificity.c += childSpecificity.c;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
// Improper use of Pseudo-Class Selectors instead of a Pseudo-Element
|
|
78
|
+
// @ref https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements#index
|
|
79
|
+
case 'after':
|
|
80
|
+
case 'before':
|
|
81
|
+
case 'first-letter':
|
|
82
|
+
case 'first-line':
|
|
83
|
+
specificity.c += 1;
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
specificity.b += 1;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case 'PseudoElementSelector':
|
|
91
|
+
switch (child.name) {
|
|
92
|
+
// “The specificity of ::slotted() is that of a pseudo-element, plus the specificity of its argument.”
|
|
93
|
+
case 'slotted':
|
|
94
|
+
specificity.c += 1;
|
|
95
|
+
if (child.children) {
|
|
96
|
+
// Workaround to a css-tree bug in which it allows complex selectors instead of only compound selectors
|
|
97
|
+
// We work around it by filtering out any Combinator and successive Selectors
|
|
98
|
+
const childAST = { type: 'Selector', children: [] };
|
|
99
|
+
let foundCombinator = false;
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
child.children.first.children.forEach((entry) => {
|
|
102
|
+
if (foundCombinator)
|
|
103
|
+
return false;
|
|
104
|
+
if (entry.type === 'Combinator') {
|
|
105
|
+
foundCombinator = true;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
childAST.children.push(entry);
|
|
109
|
+
});
|
|
110
|
+
// Calculate Specificity from Selector
|
|
111
|
+
const childSpecificity = calculate(childAST)[0];
|
|
112
|
+
// Adjust orig specificity
|
|
113
|
+
specificity.a += childSpecificity.a;
|
|
114
|
+
specificity.b += childSpecificity.b;
|
|
115
|
+
specificity.c += childSpecificity.c;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
specificity.c += 1;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'TypeSelector':
|
|
124
|
+
// Omit namespace
|
|
125
|
+
let typeSelector = child.name;
|
|
126
|
+
if (typeSelector.includes('|')) {
|
|
127
|
+
typeSelector = typeSelector.split('|')[1];
|
|
128
|
+
}
|
|
129
|
+
// “Ignore the universal selector”
|
|
130
|
+
if (typeSelector !== '*') {
|
|
131
|
+
specificity.c += 1;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
// NOOP
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return specificity;
|
|
140
|
+
};
|
|
141
|
+
const calculate = (selectorAST) => {
|
|
142
|
+
// Quit while you're ahead
|
|
143
|
+
if (!selectorAST) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
// Selector?
|
|
147
|
+
if (selectorAST.type === 'Selector')
|
|
148
|
+
return [calculateSpecificityOfSelectorObject(selectorAST)];
|
|
149
|
+
// SelectorList?
|
|
150
|
+
// ~> Calculate Specificity for each contained Selector
|
|
151
|
+
if (selectorAST.type === 'SelectorList') {
|
|
152
|
+
const specificities = [];
|
|
153
|
+
selectorAST.children.forEach((selector) => {
|
|
154
|
+
const specificity = calculateSpecificityOfSelectorObject(selector);
|
|
155
|
+
specificities.push(specificity);
|
|
156
|
+
});
|
|
157
|
+
return specificities;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
};
|
|
161
|
+
const max = (...specificities) => {
|
|
162
|
+
const sorted = sort(specificities);
|
|
163
|
+
return sorted[0];
|
|
164
|
+
};
|
|
165
|
+
const sort = (specificities) => {
|
|
166
|
+
const sorted = specificities.sort(compare);
|
|
167
|
+
return sorted.reverse();
|
|
168
|
+
};
|
|
169
|
+
const compare = (s1, s2) => {
|
|
170
|
+
if (s1.a === s2.a) {
|
|
171
|
+
if (s1.b === s2.b) {
|
|
172
|
+
return s1.c - s2.c;
|
|
173
|
+
}
|
|
174
|
+
return s1.b - s2.b;
|
|
175
|
+
}
|
|
176
|
+
return s1.a - s2.a;
|
|
177
|
+
};
|
|
178
|
+
export { calculate };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IDesignItem } from "../../item/IDesignItem.js";
|
|
2
|
+
import { IProperty } from "../propertiesService/IProperty.js";
|
|
3
|
+
import { IStyleDeclaration, IStyleRule, IStylesheetService } from "./IStylesheetService.js";
|
|
4
|
+
import * as csstree from 'css-tree';
|
|
5
|
+
import { TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
6
|
+
type RuleWithSpecificity = {
|
|
7
|
+
rule: csstree.RulePlain;
|
|
8
|
+
selector: string;
|
|
9
|
+
specificity: number;
|
|
10
|
+
};
|
|
11
|
+
export declare class StylesheetService implements IStylesheetService {
|
|
12
|
+
stylesheets: string[];
|
|
13
|
+
stylesheetChanged: TypedEvent<{
|
|
14
|
+
stylesheet: string;
|
|
15
|
+
}>;
|
|
16
|
+
styles: RuleWithSpecificity[];
|
|
17
|
+
constructor(stylesheets: string[]);
|
|
18
|
+
updateDefiningRule(designItem: IDesignItem, property: IProperty, value: string): boolean;
|
|
19
|
+
private getAppliedRulesInternal;
|
|
20
|
+
getAppliedRules(designItem: IDesignItem, prop: IProperty): IStyleRule[];
|
|
21
|
+
private getDeclarationInternal;
|
|
22
|
+
getDeclarations(designItem: IDesignItem, prop: IProperty): IStyleDeclaration[];
|
|
23
|
+
private parseStylesheetToRuleset;
|
|
24
|
+
private buildSelectorString;
|
|
25
|
+
private getSpecificity;
|
|
26
|
+
private returnRuleDeclarationIndex;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as csstree from 'css-tree';
|
|
2
|
+
import { TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
3
|
+
import { calculate as calculateSpecifity } from "./SpecificityCalculator.js";
|
|
4
|
+
export class StylesheetService {
|
|
5
|
+
stylesheets;
|
|
6
|
+
stylesheetChanged = new TypedEvent();
|
|
7
|
+
styles = [];
|
|
8
|
+
constructor(stylesheets) {
|
|
9
|
+
this.stylesheets = stylesheets;
|
|
10
|
+
}
|
|
11
|
+
updateDefiningRule(designItem, property, value) {
|
|
12
|
+
// let highestSpecificityRule = this.getAppliedRules(designItem, property);
|
|
13
|
+
// if (!highestSpecificityRule) return false;
|
|
14
|
+
// let newRule = csstree.toPlainObject(csstree.parse("* {" + property.name + ": " + value + "}")) as csstree.StyleSheetPlain;
|
|
15
|
+
// let index = this.returnRuleDeclarationIndex(highestSpecificityRule, property);
|
|
16
|
+
// if (index > -1) highestSpecificityRule.block.children.splice(index, 1, (newRule.children[0] as csstree.RulePlain).block.children[0]);
|
|
17
|
+
// else highestSpecificityRule.block.children.push((newRule.children[0] as csstree.RulePlain).block.children[0]);
|
|
18
|
+
// if (!this.ruleset) this.ruleset = this.parseStylesheetToRuleset(this.stylesheet);
|
|
19
|
+
// this.stylesheetChanged.emit({ stylesheet: csstree.generate(csstree.fromPlainObject(this.ruleset)) });
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
getAppliedRulesInternal(designItem, prop) {
|
|
23
|
+
return this.parseStylesheetToRuleset(this.stylesheets).filter(item => designItem.element.matches(item.selector));
|
|
24
|
+
}
|
|
25
|
+
getAppliedRules(designItem, prop) {
|
|
26
|
+
let rules = this.getAppliedRulesInternal(designItem, prop);
|
|
27
|
+
if (!rules)
|
|
28
|
+
return [];
|
|
29
|
+
return rules.map(r => {
|
|
30
|
+
return {
|
|
31
|
+
selector: r.selector,
|
|
32
|
+
declarations: r.rule.block.children.map(c => {
|
|
33
|
+
return {
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
name: c.property,
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
value: c.value.value,
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
important: c.important == true
|
|
40
|
+
};
|
|
41
|
+
}),
|
|
42
|
+
specificity: this.getSpecificity(r.rule.prelude)
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
getDeclarationInternal(designItem, prop) {
|
|
47
|
+
let rules = this.getAppliedRulesInternal(designItem, prop);
|
|
48
|
+
if (!rules)
|
|
49
|
+
return null;
|
|
50
|
+
let declarations = [];
|
|
51
|
+
rules.forEach(r => {
|
|
52
|
+
let index = this.returnRuleDeclarationIndex(r.rule, prop);
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
if (index > -1)
|
|
55
|
+
declarations.push(r.rule.block.children[index]);
|
|
56
|
+
});
|
|
57
|
+
return declarations;
|
|
58
|
+
}
|
|
59
|
+
getDeclarations(designItem, prop) {
|
|
60
|
+
let decl = this.getDeclarationInternal(designItem, prop);
|
|
61
|
+
if (!decl)
|
|
62
|
+
return null;
|
|
63
|
+
let declarations = [];
|
|
64
|
+
decl.forEach(d => {
|
|
65
|
+
declarations.push({
|
|
66
|
+
name: d.property,
|
|
67
|
+
value: d.value.value,
|
|
68
|
+
important: d.important == true
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return declarations;
|
|
72
|
+
}
|
|
73
|
+
parseStylesheetToRuleset(stylesheets) {
|
|
74
|
+
let styles = [];
|
|
75
|
+
stylesheets.forEach(s => {
|
|
76
|
+
let stylesheetPlain = csstree.toPlainObject(csstree.parse(s, { positions: true, parseValue: false }));
|
|
77
|
+
stylesheetPlain.children.forEach((rule) => {
|
|
78
|
+
styles.push({
|
|
79
|
+
rule: rule,
|
|
80
|
+
selector: this.buildSelectorString(s, rule.prelude),
|
|
81
|
+
specificity: this.getSpecificity(rule.prelude)
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return styles;
|
|
86
|
+
}
|
|
87
|
+
buildSelectorString(stylesheet, selector) {
|
|
88
|
+
return stylesheet.substring(selector.loc.start.offset, selector.loc.end.offset);
|
|
89
|
+
}
|
|
90
|
+
getSpecificity(selector) {
|
|
91
|
+
const specificities = calculateSpecifity(selector);
|
|
92
|
+
let sum = 0;
|
|
93
|
+
specificities.forEach(specificity => sum += specificity.a * 10000 + specificity.b * 100 + specificity.c);
|
|
94
|
+
return sum;
|
|
95
|
+
}
|
|
96
|
+
returnRuleDeclarationIndex(rule, property) {
|
|
97
|
+
let decl;
|
|
98
|
+
rule.block.children.forEach((child) => {
|
|
99
|
+
if (child.property == property.name) {
|
|
100
|
+
decl = child;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (!decl)
|
|
105
|
+
return -1;
|
|
106
|
+
return rule.block.children.indexOf(decl);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -32,6 +32,7 @@ export declare class DesignerCanvas extends BaseCustomWebComponentLazyAppend imp
|
|
|
32
32
|
private _zoomFactor;
|
|
33
33
|
private _scaleFactor;
|
|
34
34
|
private _canvasOffset;
|
|
35
|
+
private _additionalStyle;
|
|
35
36
|
private _currentContextMenu;
|
|
36
37
|
private _backgroundImage;
|
|
37
38
|
get zoomFactor(): number;
|
|
@@ -52,6 +53,7 @@ export declare class DesignerCanvas extends BaseCustomWebComponentLazyAppend imp
|
|
|
52
53
|
private _firstConnect;
|
|
53
54
|
private _onKeyDownBound;
|
|
54
55
|
private _onKeyUpBound;
|
|
56
|
+
private cssprefixConstant;
|
|
55
57
|
static readonly style: CSSStyleSheet;
|
|
56
58
|
static readonly template: HTMLTemplateElement;
|
|
57
59
|
extensionManager: IExtensionManager;
|
|
@@ -66,6 +68,8 @@ export declare class DesignerCanvas extends BaseCustomWebComponentLazyAppend imp
|
|
|
66
68
|
get designerOffsetWidth(): number;
|
|
67
69
|
get designerOffsetHeight(): number;
|
|
68
70
|
set additionalStyles(value: CSSStyleSheet[]);
|
|
71
|
+
get additionalStyles(): CSSStyleSheet[];
|
|
72
|
+
private applyAllStyles;
|
|
69
73
|
executeCommand(command: IUiCommand): Promise<void>;
|
|
70
74
|
disableBackgroud(): void;
|
|
71
75
|
enableBackground(): void;
|
|
@@ -121,4 +125,6 @@ export declare class DesignerCanvas extends BaseCustomWebComponentLazyAppend imp
|
|
|
121
125
|
zoomPoint(canvasPoint: IPoint, newZoom: number): void;
|
|
122
126
|
private zoomConvertEventToViewPortCoordinates;
|
|
123
127
|
zoomTowardsPoint(canvasPoint: IPoint, newZoom: number): void;
|
|
128
|
+
private buildPatchedStyleSheet;
|
|
129
|
+
private traverseAndCollectRules;
|
|
124
130
|
}
|