Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes/jochen #400

Merged
merged 12 commits into from
Jan 12, 2024
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"description": "web-component-designer addon: Parser Service for Templates inside of BaseCustomWebcomponent",
"name": "@node-projects/web-component-designer-htmlparserservice-base-custom-webcomponent",
"version": "0.1.3",
"version": "0.1.5",
"type": "module",
"main": "./dist/service/htmlParserService/BaseCustomWebcomponentParserService.js",
"author": "jochen.kuehner@gmx.de",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { IHtmlParserService, ServiceContainer, InstanceServiceContainer, IDesignItem } from '@node-projects/web-component-designer';
import ts, { SourceFile } from 'typescript'
//import ts, { SourceFile } from 'typescript'
import type ts from 'typescript'
import type { SourceFile } from 'typescript'

function* findAllNodesOfKind(node: ts.Node, kind: ts.SyntaxKind) {
if (node.kind == kind)
Expand All @@ -19,19 +21,26 @@ export class BaseCustomWebcomponentParserService implements IHtmlParserService {
const sourceFile = this.parseTypescriptFile(code);

let htmlCode = "";
let cssStyle = "";
let cssStyle = "";
let positionOffset = 0;
//let cssOffset = 0;
//@ts-ignore
const nodes = findAllNodesOfKind(sourceFile, ts.SyntaxKind.TaggedTemplateExpression);
for (let nd of nodes) {
if (nd.tag.escapedText == 'html' && nd.parent.name.escapedText == "template")
if (nd.tag.escapedText == 'html' && nd.parent.name.escapedText == "template") {
positionOffset = nd.pos;
htmlCode = nd.template.rawText;
if (nd.tag.escapedText == 'css' && nd.parent.name.escapedText == "style")
}
if (nd.tag.escapedText == 'css' && nd.parent.name.escapedText == "style") {
//cssOffset = nd.pos;
cssStyle = nd.template.rawText;
}
}

if (cssStyle)
instanceServiceContainer.stylesheetService.setStylesheets([{ name: 'css', content: cssStyle }]);

return this.htmlParser.parse(htmlCode, serviceContainer, instanceServiceContainer, parseSnippet);
return this.htmlParser.parse(htmlCode, serviceContainer, instanceServiceContainer, parseSnippet, positionOffset);
}

public writeBack(code: string, html: string, css: string, newLineCrLf: boolean): string {
Expand All @@ -41,26 +50,36 @@ export class BaseCustomWebcomponentParserService implements IHtmlParserService {
const transformTemplateLiterals = <T extends ts.Node>(context: ts.TransformationContext) =>
(rootNode: T) => {
function visit(node: ts.Node): ts.Node {


//@ts-ignore
if (ts.isTemplateLiteral(node) &&
//@ts-ignore
ts.isTaggedTemplateExpression(node.parent) &&
(<any>node.parent.tag).escapedText == 'html' &&
(<any>node.parent.parent).name.escapedText == "template") {
//@ts-ignore
return <ts.Node>ts.factory.createNoSubstitutionTemplateLiteral(html.replaceAll('\n', '\r\n'), html.replaceAll('\n', '\r\n'));
} else if (css &&
//@ts-ignore
ts.isTemplateLiteral(node) &&
//@ts-ignore
ts.isTaggedTemplateExpression(node.parent) &&
(<any>node.parent.tag).escapedText == 'css' &&
(<any>node.parent.parent).name.escapedText == "style") {
//@ts-ignore
return <ts.Node>ts.factory.createNoSubstitutionTemplateLiteral(css.replaceAll('\n', '\r\n'), css.replaceAll('\n', '\r\n'));
}
}
//@ts-ignore
return ts.visitEachChild(node, visit, context);
}
//@ts-ignore
return ts.visitNode(rootNode, visit);
};
//@ts-ignore
let transformed = ts.transform(sourceFile, [transformTemplateLiterals]).transformed[0];

//@ts-ignore
const printer = ts.createPrinter({ newLine: newLineCrLf ? ts.NewLineKind.CarriageReturnLineFeed : ts.NewLineKind.LineFeed });
//@ts-ignore
const result = printer.printNode(ts.EmitHint.Unspecified, transformed, <SourceFile>transformed);

return result;
Expand All @@ -74,6 +93,7 @@ export class BaseCustomWebcomponentParserService implements IHtmlParserService {
getDefaultLibFileName: () => 'lib.d.ts',
getNewLine: () => '\n',
getSourceFile: filename => {
//@ts-ignore
return ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true);
},
readFile: () => null,
Expand All @@ -82,8 +102,10 @@ export class BaseCustomWebcomponentParserService implements IHtmlParserService {
};

const filename = 'aa.ts';
//@ts-ignore
const program = ts.createProgram([filename], {
noResolve: true,
//@ts-ignore
target: ts.ScriptTarget.Latest,
}, compilerHost);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"description": "web-component-designer addon: Parser Service using the Node HTML Parser",
"name": "@node-projects/web-component-designer-htmlparserservice-nodehtmlparser",
"version": "0.1.4",
"version": "0.1.9",
"type": "module",
"main": "./dist/service/htmlParserService/NodeHtmlParserService.js",
"author": "jochen.kuehner@gmx.de",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ export class NodeHtmlParserService implements IHtmlParserService {
this._designItemCreatedCallback = designItemCreatedCallback;
}

async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {
async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean, positionOffset = 0): Promise<IDesignItem[]> {
const parsed = parser.parse(html, { comment: true });

let designItems: IDesignItem[] = [];
for (let p of parsed.childNodes) {
let di = this._createDesignItemsRecursive(p, serviceContainer, instanceServiceContainer, null, parseSnippet);
let di = this._createDesignItemsRecursive(p, serviceContainer, instanceServiceContainer, null, parseSnippet, positionOffset);

if (di != null)
designItems.push(di)
Expand All @@ -27,7 +27,7 @@ export class NodeHtmlParserService implements IHtmlParserService {

private _parseDiv = document.createElement("div");

_createDesignItemsRecursive(item: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, namespace: string, snippet: boolean): IDesignItem {
_createDesignItemsRecursive(item: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, namespace: string, snippet: boolean, positionOffset = 0): IDesignItem {
let designItem: IDesignItem = null;
if (item.nodeType == 1) {
let element: Element;
Expand All @@ -45,7 +45,7 @@ export class NodeHtmlParserService implements IHtmlParserService {
if (this._designItemCreatedCallback)
this._designItemCreatedCallback(designItem);
if (!snippet && instanceServiceContainer.designItemDocumentPositionService)
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0], length: item.range[1] - item.range[0] });
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });

let style = '';

Expand Down Expand Up @@ -77,20 +77,29 @@ export class NodeHtmlParserService implements IHtmlParserService {
(<HTMLElement>element).draggable = false; //even if it should be true, for better designer exp.

for (let c of item.childNodes) {
let di = this._createDesignItemsRecursive(c, serviceContainer, instanceServiceContainer, element instanceof SVGElement ? 'http://www.w3.org/2000/svg' : null, snippet);
let di = this._createDesignItemsRecursive(c, serviceContainer, instanceServiceContainer, element instanceof SVGElement ? 'http://www.w3.org/2000/svg' : null, snippet, positionOffset);
designItem._insertChildInternal(di);

if (di.node instanceof HTMLTemplateElement && di.getAttribute('shadowrootmode') == 'open') {
try {
const shadow = (<HTMLElement>designItem.node).attachShadow({ mode: 'open' });
shadow.appendChild(di.node.content.cloneNode(true));
} catch (err) {
console.error("error attaching shadowdom", err)
}
}
}
} else if (item.nodeType == 3) {
this._parseDiv.innerHTML = item.rawText;
let element = this._parseDiv.childNodes[0];
designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);
if (!snippet && instanceServiceContainer.designItemDocumentPositionService)
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0], length: item.range[1] - item.range[0] });
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });
} else if (item.nodeType == 8) {
let element = document.createComment(item.rawText);
designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);
if (!snippet && instanceServiceContainer.designItemDocumentPositionService)
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0], length: item.range[1] - item.range[0] });
instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });
}
return designItem;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/web-component-designer/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"description": "A UI designer for Polymer apps",
"name": "@node-projects/web-component-designer",
"version": "0.1.45",
"version": "0.1.51",
"type": "module",
"main": "./dist/index.js",
"author": "jochen.kuehner@gmx.de",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ export function getElementCombinedTransform(element: HTMLElement): DOMMatrix {
let s = getComputedStyle(element);

let m = new DOMMatrix();
if (s.translate != 'none') {
if (s.translate != 'none' && s.translate) {
m = m.multiply(new DOMMatrix('translate(' + s.translate + ')'));
}
if (s.rotate != 'none') {
if (s.rotate != 'none' && s.rotate) {
m = m.multiply(new DOMMatrix('rotate(' + s.rotate + ')'));
}
if (s.scale != 'none') {
if (s.scale != 'none' && s.scale) {
m = m.multiply(new DOMMatrix('scale(' + s.scale + ')'));
}
if (s.transform != 'none') {
if (s.transform != 'none' && s.transform) {
m = m.multiply(new DOMMatrix(s.transform));
}
return m;
Expand Down Expand Up @@ -148,7 +148,7 @@ export function getResultingTransformationBetweenElementAndAllAncestors(element:
let actualElementMatrix: DOMMatrix;
let newElementMatrix: DOMMatrix;
let originalElementAndAllParentsMultipliedMatrix: DOMMatrix;
while (actualElement != ancestor) {
while (actualElement != ancestor && actualElement != null) {
let cachedObj = ch.get(actualElement);
if (cachedObj) {
if (originalElementAndAllParentsMultipliedMatrix)
Expand All @@ -160,19 +160,21 @@ export function getResultingTransformationBetweenElementAndAllAncestors(element:
}

const newElement = <HTMLElement>getParentElementIncludingSlots(actualElement);
actualElementMatrix = getElementCombinedTransform((<HTMLElement>actualElement));
newElementMatrix = getElementCombinedTransform((<HTMLElement>newElement));
newElementMatrix.m41 = newElementMatrix.m42 = 0;
if (actualElement == element) {
originalElementAndAllParentsMultipliedMatrix = actualElementMatrix.multiply(newElementMatrix);
} else if (newElement != ancestor || !excludeAncestor) {
originalElementAndAllParentsMultipliedMatrix = originalElementAndAllParentsMultipliedMatrix.multiply(newElementMatrix);
}
if (newElement) {
actualElementMatrix = getElementCombinedTransform((<HTMLElement>actualElement));
newElementMatrix = getElementCombinedTransform((<HTMLElement>newElement));
newElementMatrix.m41 = newElementMatrix.m42 = 0;
if (actualElement == element) {
originalElementAndAllParentsMultipliedMatrix = actualElementMatrix.multiply(newElementMatrix);
} else if (newElement != ancestor || !excludeAncestor) {
originalElementAndAllParentsMultipliedMatrix = originalElementAndAllParentsMultipliedMatrix.multiply(newElementMatrix);
}

lst.forEach(x => x[0] = x[0].multiply(originalElementAndAllParentsMultipliedMatrix));
const cacheEntry: [DOMMatrix] = [originalElementAndAllParentsMultipliedMatrix];
lst.push(cacheEntry);
ch.set(actualElement, cacheEntry);
lst.forEach(x => x[0] = x[0].multiply(originalElementAndAllParentsMultipliedMatrix));
const cacheEntry: [DOMMatrix] = [originalElementAndAllParentsMultipliedMatrix];
lst.push(cacheEntry);
ch.set(actualElement, cacheEntry);
}

actualElement = newElement;
}
Expand All @@ -187,6 +189,8 @@ export function getByParentsTransformedPointRelatedToCanvas(element: HTMLElement
let byParentTransformedPointRelatedToCanvas: IPoint = { x: 0, y: 0 };
while (actualElement != canvas) {
const parentElement = <HTMLElement>getParentElementIncludingSlots(actualElement);
if (!parentElement)
break;
const elementWindowOffset = getElementsWindowOffsetWithoutSelfAndParentTransformations(parentElement, designerCanvas.zoomFactor, cache);

const toSplit = getComputedStyle(<HTMLElement>parentElement).transformOrigin.split(' ');
Expand Down
47 changes: 35 additions & 12 deletions packages/web-component-designer/src/elements/item/DesignItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import { DeleteAction } from '../services/undoService/transactionItems/DeleteAct
import { IDesignerExtensionProvider } from '../widgets/designerView/extensions/IDesignerExtensionProvider.js';
import { IStyleRule } from '../services/stylesheetService/IStylesheetService.js';
import { enableStylesheetService } from '../widgets/designerView/extensions/buttons/StylesheetServiceDesignViewConfigButtons.js';
import { AbstractStylesheetService } from '../services/stylesheetService/AbstractStylesheetService.js';
import { TypedEvent, cssFromString } from '@node-projects/base-custom-webcomponent';
import { TypedEvent } from '@node-projects/base-custom-webcomponent';
import { IPlacementService } from '../services/placementService/IPlacementService.js';
import { TextContentChangeAction } from '../services/undoService/transactionItems/TextContentChangeAction.js';

Expand Down Expand Up @@ -258,10 +257,10 @@ export class DesignItem implements IDesignItem {

//abstract text content to own property. so only change via designer api will use it.
public get hasContent() {
return this.nodeType == NodeType.TextNode || (this.nodeType == NodeType.Comment && this.element.textContent != "") || (this._childArray.length === 0 && this.content !== null);
return ((this.nodeType == NodeType.TextNode || this.nodeType == NodeType.Comment) && this.element.textContent != "") || (this._childArray.length === 0);
}
public get content(): string {
if (!this.hasChildren)
if (this.nodeType == NodeType.TextNode || this.nodeType == NodeType.Comment)
return this.node.textContent;
else
return this._childArray.map(x => x.content).join();
Expand Down Expand Up @@ -338,6 +337,15 @@ export class DesignItem implements IDesignItem {
public static createDesignItemFromInstance(node: Node, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): DesignItem {
let designItem = new DesignItem(node, node, serviceContainer, instanceServiceContainer);

if (node instanceof HTMLTemplateElement && node.getAttribute('shadowrootmode') == 'open') {
try {
const shadow = (<HTMLElement>node.parentNode).attachShadow({ mode: 'open' });
shadow.appendChild(node.content.cloneNode(true));
} catch (err) {
console.error("error attaching shadowdom", err)
}
}

if (designItem.nodeType == NodeType.Element) {
for (let a of designItem.element.attributes) {
if (a.name !== 'style') {
Expand Down Expand Up @@ -371,10 +379,18 @@ export class DesignItem implements IDesignItem {
updateChildrenFromNodesChildren() {
this._childArray = [];
if (this.nodeType == NodeType.Element) {
for (const c of this.element.childNodes) {
const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);
this._childArray.push(di);
(<DesignItem>di)._parent = this;
if (this.element instanceof HTMLTemplateElement) {
for (const c of this.element.content.childNodes) {
const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);
this._childArray.push(di);
(<DesignItem>di)._parent = this;
}
} else {
for (const c of this.element.childNodes) {
const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);
this._childArray.push(di);
(<DesignItem>di)._parent = this;
}
}
}
this._refreshIfStyleSheet();
Expand Down Expand Up @@ -555,10 +571,16 @@ export class DesignItem implements IDesignItem {

if (index == null || this._childArray.length == 0 || index >= this._childArray.length) {
this._childArray.push(designItem);
this.view.appendChild(designItem.view);
if (this.view instanceof HTMLTemplateElement) {
this.view.content.appendChild(designItem.view)
} else
this.view.appendChild(designItem.view);
} else {
let el = this._childArray[index];
this.view.insertBefore(designItem.view, el.element)
if (this.view instanceof HTMLTemplateElement) {
this.view.content.insertBefore(designItem.view, el.element)
} else
this.view.insertBefore(designItem.view, el.element)
this._childArray.splice(index, 0, designItem);
}
(<DesignItem>designItem)._parent = this;
Expand Down Expand Up @@ -594,9 +616,10 @@ export class DesignItem implements IDesignItem {
//Update Stylesheetservice with sheet info
//Patch this sheetdata

const realContent = this._childArray.reduce((a, b) => a + b.content, '');
//TODO: do not patch styles in templates, this needs to be recursive, cause the style does not need to be on the root
/*const realContent = this._childArray.reduce((a, b) => a + b.content, '');
this.view.textContent = AbstractStylesheetService.buildPatchedStyleSheet([cssFromString(realContent)]);
this.instanceServiceContainer.designerCanvas.lazyTriggerReparseDocumentStylesheets();
this.instanceServiceContainer.designerCanvas.lazyTriggerReparseDocumentStylesheets();*/
} else if (this.name == 'link') {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ export class SimpleDemoProviderService implements IDemoProviderService {
.map(x => cssFromString(x.content)));
}
shadowRoot.adoptedStyleSheets = styles;
shadowRoot.innerHTML = code;
shadowRoot.innerHTML = '';
//@ts-ignore
const myDocument = new DOMParser().parseFromString(code, 'text/html', { includeShadowRoots: true });
const fragment = document.createDocumentFragment();
for (let e of myDocument.childNodes)
fragment.appendChild(e)
shadowRoot.appendChild(fragment)
contentDiv.style.display = '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class DefaultHtmlParserService implements IHtmlParserService {
public createDesignItems(elements: NodeListOf<ChildNode> | Node[] | HTMLCollection | HTMLElement[], serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer) {
let res: IDesignItem[] = [];
for (let el of elements) {
res.push(this._createDesignItemsRecursive(el, serviceContainer, instanceServiceContainer))
res.push(this._createDesignItemsRecursive(el, serviceContainer, instanceServiceContainer));
}
return res;
}
Expand Down
Loading