From 8460743ee8acdeebb726a4f974e0b446f9600cc1 Mon Sep 17 00:00:00 2001 From: yaoyuchi Date: Tue, 18 Jan 2022 12:05:52 +0800 Subject: [PATCH 1/4] support css selector Signed-off-by: yaoyuchi --- runtime/main/model/compiler.ts | 82 ++++++- runtime/main/model/directive.ts | 364 +++++++++++++++++++++++++++++++- runtime/vdom/Element.ts | 73 ++++++- 3 files changed, 504 insertions(+), 15 deletions(-) diff --git a/runtime/main/model/compiler.ts b/runtime/main/model/compiler.ts index 07dcfca4..f86355a0 100644 --- a/runtime/main/model/compiler.ts +++ b/runtime/main/model/compiler.ts @@ -41,7 +41,8 @@ import { bindSubVm, bindSubVmAfterInitialized, newWatch, - bindDir + bindDir, + updateTagCounter } from './directive'; import { createBlock, @@ -58,6 +59,7 @@ import Vm from './index'; import Element from '../../vdom/Element'; import Comment from '../../vdom/Comment'; import Node from '../../vdom/Node'; +import Document from '../../vdom/Document'; import { VmOptions } from './vmOptions'; export interface FragBlockInterface { @@ -80,6 +82,7 @@ export interface AttrInterface { tid: number; append: string; slot: string; + slotScope: string; name: string; data: () => any | string; $data: () => any | string; @@ -124,6 +127,12 @@ export function build(vm: Vm) { const opt: any = vm._vmOptions || {}; const template: any = opt.template || {}; compile(vm, template, vm._parentEl); + // foreach vm + const doc: Document = vm._app.doc; + const body: Node = doc.body; + compileVm(vm, body); + compileCounter(vm, body); + compileElementAndElement(vm, body); Log.debug(`"OnReady" lifecycle in Vm(${vm._type}).`); vm.$emit('hook:onReady'); if (vm._parent) { @@ -183,6 +192,66 @@ function compile(vm: Vm, target: TemplateInterface, dest: FragBlockInterface | E compileNativeComponent(vm, target, dest, type); } +function compileVm(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + let count = 0; + node.children.forEach((child: Node) => { + const el = child as Element; + const tag = child.type; + if (count === 0) { + setTagStyle(vm, el, tag, true, false, false); + } else if (count === node.children.length - 1) { + setTagStyle(vm, el, tag, false, true, false); + } + count++; + compileVm(vm, child); + }); + } +} + +function compileCounter(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + let count = {}; + + node.children.forEach((child: Node) => { + const el = child as Element; + const tag = child.type; + if (count[tag] == undefined) { + count[tag] = 1; + } else { + count[tag] = count[tag] + 1; + } + let css = vm._css || {}; + if (css) { + let data = css[tag] || {}; + if (data){ + let counterIncrement = data['counterIncrement']; + if (counterIncrement !== undefined) { + updateTagCounter(el, count[tag]); + } + } + } + compileCounter(vm, child); + }); + } +} + +function compileElementAndElement(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + node.children.forEach((child: Node) => { + if (child.nextSibling) { + const el = child.nextSibling as Element; + const tag = child.type + '+' + child.nextSibling.type + setTagStyle(vm, el, tag, false, false, false); + } + compileElementAndElement(vm, child) + }); + } +} + /** * Compile a dynamic component. * @param {Vm} vm - Vm object needs to be compiled. @@ -316,6 +385,15 @@ function compileSlot(vm: Vm, target: TemplateInterface, dest: Element): Element if (!namedContent) { compileChildren(vm, slotItem.target, slotItem.dest); } else { + // Bind slot scope + if (Array.isArray(namedContent)) { + namedContent.forEach((item: TemplateInterface) => { + const slotScope = item.attr && item.attr.slotScope; + if (typeof slotScope === 'string') { + parentVm[slotScope] = vm._data; + } + }); + } compileChildren(parentVm, { children: namedContent }, slotItem.dest); } } @@ -484,7 +562,7 @@ function resetElementStyle(vm: Vm, element: Element): void { } setUniversalStyle(vm, element); if (element.type) { - setTagStyle(vm, element, element.type); + setTagStyle(vm, element, element.type, false, false, false); } if (element.id) { setIdStyle(vm, element, element.id); diff --git a/runtime/main/model/directive.ts b/runtime/main/model/directive.ts index 47cb24cd..f3f221c1 100644 --- a/runtime/main/model/directive.ts +++ b/runtime/main/model/directive.ts @@ -48,6 +48,7 @@ import { } from './compiler'; import Vm from './index'; import Element from '../../vdom/Element'; +import Node from '../../vdom/Node'; const SETTERS = { attr: 'setAttr', @@ -57,9 +58,19 @@ const SETTERS = { event: 'addEvent', idStyle: 'setIdStyle', tagStyle: 'setTagStyle', + tagAndTagStyle: 'setTagAndTagStyle', + tagAndIdStyle: 'setTagAndIdStyle', universalStyle: 'setUniversalStyle' }; + +enum ContentType {Content_String, Content_Open_Quote, Content_Close_Quote, Content_Attr, Content_Counter}; +interface ContentObject { + value: string, + contentType: ContentType +} +let finallyItems: Array = []; + /** * Bind id, attr, classnames, style, events to an element. * @param {Vm} vm - Vm object. @@ -109,7 +120,8 @@ export function bindElement(vm: Vm, el: Element, template: TemplateInterface, pa setStyle(vm, el, template.style); setIdStyle(vm, el, template.id); setClass(vm, el, template.classList); - setTagStyle(vm, el, template.type); + setTagStyle(vm, el, template.type, false, false, true); + setTagAndIdStyle(vm, el, template.type, template.id); setUniversalStyle(vm, el); applyStyle(vm, el); @@ -464,6 +476,11 @@ function selectIdStyle(css: object, id: string, vm: Vm): any { return selectStyle(css, key, vm); } +function selectTagAndIdStyle(css: object, tag: string, id: string, vm: Vm): any { + const key = tag + '#' + id; + return selectStyle(css, key, vm); +} + /** * Replace style. * @param {*} oStyle - Current style. @@ -668,7 +685,7 @@ export function setIdStyle(vm: Vm, el: Element, id: Function | string): void { * @param {*} css - Css style. * @param {string} name - Bind by name. */ -function doSetStyle(vm: Vm, el: Element, style: any, css: any, name: string): void { +function doSetStyle(vm: Vm, el: Element, style: any, css: any, name: string, isFirst?: boolean, isLast?: boolean, isSetContent?: boolean): void { if (!style) { return; } @@ -676,7 +693,7 @@ function doSetStyle(vm: Vm, el: Element, style: any, css: any, name: string): vo Object.assign(typeStyle, style); setAnimation(typeStyle, css); setFontFace(typeStyle, css); - bindDir(vm, el, name, typeStyle); + bindDir(vm, el, name, typeStyle, isFirst, isLast, isSetContent); } /** @@ -714,10 +731,23 @@ function setAnimation(style: any, css: any): void { * @param {Element} el - ELement component. * @param {string} tag - Tag. */ -export function setTagStyle(vm: Vm, el: Element, tag: string): void { +export function setTagStyle(vm: Vm, el: Element, tag: string, isFirst?: boolean, isLast?: boolean, isSetContent?: boolean): void { const css = vm._css || {}; if (tag && typeof tag === 'string') { - doSetStyle(vm, el, selectStyle(css, tag, vm), css, 'tagStyle'); + let tagStyle = 'tagStyle'; + if (tag.indexOf('+') > 0) { + tagStyle = 'tagAndTagStyle'; + } + doSetStyle(vm, el, selectStyle(css, tag, vm), css, tagStyle, isFirst, isLast, isSetContent); + } +} + +export function setTagAndIdStyle(vm: Vm, el: Element, tag: string, id: Function | string): void { + const css = vm._css || {}; + if (typeof id === 'string') { + if (tag && typeof tag === 'string') { + doSetStyle(vm, el, selectTagAndIdStyle(css, tag, id, vm), css, 'tagAndIdStyle'); + } } } @@ -728,7 +758,7 @@ export function setTagStyle(vm: Vm, el: Element, tag: string): void { */ export function setUniversalStyle(vm: Vm, el: Element): void { const css = vm._css || {}; - doSetStyle(vm, el, selectStyle(css, "*", vm), css, 'universalStyle'); + doSetStyle(vm, el, selectStyle(css, '*', vm), css, 'universalStyle'); } /** @@ -788,26 +818,72 @@ function bindEvents(vm: Vm, el: Element, events: object, eventType?: string): vo * @param {string} name - Method name. * @param {Object} data - Data that needed. */ -export function bindDir(vm: Vm, el: Element, name: string, data: object): void { +export function bindDir(vm: Vm, el: Element, name: string, data: object, isFirst?: boolean, isLast?: boolean, isSetContent?: boolean): void { if (!data) { return; } - const keys = Object.keys(data); + let keys = Object.keys(data); let i = keys.length; if (!i) { return; } - const methodName = SETTERS[name]; - const method = el[methodName]; + let methodName = SETTERS[name]; + let method = el[methodName]; const isSetStyle = methodName === 'setStyle'; if (methodName === 'setIdStyle') { for (const id in el.idStyle) { el.idStyle[id] = ''; } } + if (name === 'tagStyle' || name === 'tagAndIdStyle') { // ??style keys??DD??D¨°¡ê???content::before???¨²content::after???¡ă + let j: number = 0; + let k: number = 0; + let temp: string = null; + for (j = 0; j < i - 1; j++) { + for (k = 0; k < i - 1 - j; k++) { + if (keys[k] > keys[k + 1]) { + temp = keys[k + 1]; + keys[k + 1] = keys[k]; + keys[k] = temp; + } + } + } + } + let isOpen = false; while (i--) { - const key = keys[i]; + let key = keys[i]; const value = data[key]; + if (name === 'tagStyle') { + if (key.endsWith(':first-child') || key.endsWith(':last-child')) { + methodName = SETTERS['firstOrLastChildStyle']; + } else { + methodName = SETTERS[name]; + } + if (key.endsWith(':first-child') && isFirst) { + key = key.replace(':first-child', ''); + } else if (key.endsWith(':last-child') && isLast) { + key = key.replace(':last-child', ''); + } + + if (isSetContent && (key === 'content::before' || key === 'content::after')) { + finallyItems = []; + splitItems(value); + let newValue = setContent(el, key); + methodName = SETTERS['attr']; + method = el[methodName]; + method.call(el, 'value', newValue); + continue; + } + } + + if (name === 'tagAndIdStyle') { + let newValue = updateTagAndIdStyle(el, key, value); + methodName = SETTERS['attr']; + method = el[methodName]; + method.call(el, 'value', newValue); + continue; + } + method = el[methodName]; if (key === 'ref') { vm.$refs[value] = el; } @@ -914,3 +990,269 @@ function applyStyle(vm: Vm, el: Element): void { function isArray(params: any): params is Array { return Array.isArray(params); } + +function splitItems(valueStr: string): void { + let i: number; + let item: string = ''; + let startQuote: boolean = false; + let itemList: string[] = []; + let len = valueStr.length; + for (i = 0; i < len; i++) { + if (!startQuote) { + if (valueStr[i] === '"') { + const itemLength = item.length; + if (itemLength > 0) { + const itemListLength = itemList.length; + itemList[itemListLength] = item; + } + item = '"'; + startQuote = true; + continue; + } else { + item = item + valueStr[i]; + if (i == len - 1) { + const itemListLength = itemList.length; + itemList[itemListLength] = item; + } + continue; + } + } else { + if (valueStr[i] === '"') { + item = item + valueStr[i]; + startQuote = false; + const itemListLength = itemList.length; + itemList[itemListLength] = item; + item = ''; + continue; + } else { + item = item + valueStr[i]; + continue; + } + } + } + doSplitItem(itemList); +} + +function doSplitItem(itemList: string[]): void { + let i: number; + let isValidate: boolean = true; + let itemListLength = itemList.length; + for (i = 0; i < itemListLength; i++ ) { + let item = itemList[i].trim(); + if (item.indexOf('"') === 0) { + item = item.replace('"', ''); + item = item.replace('"', ''); + let contentObject: ContentObject = { + value: item, + contentType: ContentType.Content_String + } + const finallyItemsLength = finallyItems.length; + finallyItems[finallyItemsLength] = contentObject; + } else { + splitItem(item.trim()); + if (finallyItems.length > 0) { + continue; + } else { + return; + } + } + } + if (finallyItems.length > 0) { + let i: number; + const finallyItemsLength = finallyItems.length; + for (i = 0;i < finallyItemsLength; i++) { + } + } +} + +function splitItem(item: string): void{ + if (item.length == 0) { + return; + } + let finallyItemsLength = finallyItems.length; + if (item.indexOf('open-quote') === 0) { + let subItem = item.substr(0, 10); + let contentObject: ContentObject = { + value: subItem, + contentType: ContentType.Content_Open_Quote + } + + finallyItems[finallyItemsLength] = contentObject; + splitItem(item.substr(10).trim()); + } else if (item.indexOf('close-quote') === 0) { + let subItem = item.substr(0, 11); + let contentObject: ContentObject = { + value: subItem, + contentType: ContentType.Content_Close_Quote + } + finallyItems[finallyItemsLength] = contentObject; + splitItem(item.substr(11).trim()); + } else if (item.indexOf('attr') === 0) { + let fromIndex = item.indexOf('('); + let toIndex = item.indexOf(')'); + let subLen = toIndex - fromIndex - 1; + let subItem = item.substr(fromIndex + 1, subLen).trim(); + let contentObject: ContentObject = { + value: subItem, + contentType: ContentType.Content_Attr + } + finallyItems[finallyItemsLength] = contentObject; + splitItem(item.substr(toIndex + 1).trim()); + } else if (item.indexOf('counter') === 0) { + let fromIndex = item.indexOf('('); + let toIndex = item.indexOf(')'); + let subItem = 'counter(0)'; + let contentObject: ContentObject = { + value: subItem, + contentType: ContentType.Content_Counter + } + finallyItems[finallyItemsLength] = contentObject; + splitItem(item.substr(toIndex + 1).trim()); + } else { + finallyItems = []; + } +} + +function setContent(el: Element, key: string): string { + let itemLength = finallyItems.length; + let contentValue = ''; + let newValue = ''; + if (itemLength > 0) { + let i: number; + for (i = 0; i < itemLength; i++) { + const contentType = finallyItems[i].contentType; + switch (contentType) { + case ContentType.Content_String: + contentValue = contentValue + getContentString(finallyItems[i].value); + break; + case ContentType.Content_Open_Quote: + contentValue = contentValue + getContentOpenQuote(el, key); + break; + case ContentType.Content_Close_Quote: + contentValue = contentValue + getContentCloseQuote(el, key); + break; + case ContentType.Content_Attr: + contentValue = contentValue + getContentAttr(el, finallyItems[i].value); + break; + case ContentType.Content_Counter: + contentValue = contentValue + finallyItems[i].value; + break; + } + } + const oldValue = el.attr['value']; + + if (key === 'content::before') { + newValue = contentValue + oldValue; + } else if (key === 'content::after') { + newValue = oldValue + contentValue; + } + } + return newValue; +} + +function getContentString(value: string): string { + let contentValue = value.replace('\"', ''); + contentValue = contentValue.replace('\"', ''); + return contentValue; +} + +function getContentOpenQuote(el: Element, key: string): string { + let contentValue = ''; + if (el.isOpen) { + contentValue = '\''; + } else { + contentValue = '\"'; + el.isOpen = true; + } + if (key === 'content::before') { + el.hasBefore = true; + } + return contentValue; +} + +function getContentCloseQuote(el: Element, key: string): string { + let contentValue = ''; + if (el.isOpen) { + contentValue = '\"'; + } else { + contentValue = ''; + } + if (el.isOpen && key === 'content::after') { + el.hasAfter = true; + } + return contentValue; +} + +function getContentAttr(el: Element, value: string): string { + let contentValue = el.attr[value]; + if (contentValue === undefined) { + contentValue = ''; + } + return contentValue; +} + +export function updateTagCounter(el: Element, counter: number): void { + let value = el.attr['value']; + if (value !== undefined) { + let fromIndex = value.indexOf('counter'); + if (fromIndex !== -1) { + value = value.replace('counter(0)', counter); + let methodName = SETTERS['attr']; + let method = el[methodName]; + method.call(el, 'value', value); + } + } +} + +function updateTagAndIdStyle(el: Element, key: string, value: string): string { + let newValue = ''; + let contentValue = ''; + let oldValue = el.attr['value']; + if (key === 'content::before') { + if (value === 'open-quote' || value === 'close-quote') { + if (value === 'open-quote' && key === 'content::before') { + contentValue = '\"'; + if (el.hasBefore) { + oldValue = oldValue.substr(1, oldValue.length); + } + newValue = contentValue + oldValue; + oldValue = newValue; + } else if (el.hasBefore && value === 'close-quote' && key === 'content::before') { + el.hasBefore = false; + oldValue = oldValue.substr(1, oldValue.length); + newValue = oldValue; + } + } + } else if (key === 'content::after') { + if (value === 'open-quote' && key === 'content::after') { + contentValue = '\"'; + if (el.hasBefore) { + contentValue = '\''; + } + if (el.hasAfter) { + oldValue = oldValue.substr(0, oldValue.length - 1); + } + newValue = oldValue + contentValue; + } else if (value === 'close-quote' && key === 'content::after' && el.hasBefore) { + contentValue = '\"'; + if (el.hasAfter) { + oldValue = oldValue.substr(0, oldValue.length - 1); + } + newValue = oldValue + contentValue; + } + } + if (value === 'no-open-quote' || value === 'no-close-quote') { + newValue = oldValue; + if (key === 'content::before' && value === 'no-open-quote') { + if (el.hasBefore) { + newValue = oldValue.substr(1); + } + } + if (key === 'content::after') { + if (el.hasAfter) { + newValue = oldValue.substr(0, oldValue.length - 1); + } + } + } + return newValue; +} \ No newline at end of file diff --git a/runtime/vdom/Element.ts b/runtime/vdom/Element.ts index 83a7bc07..f1773dc8 100644 --- a/runtime/vdom/Element.ts +++ b/runtime/vdom/Element.ts @@ -47,6 +47,8 @@ class Element extends Node { private _event: any; private _idStyle: any; private _tagStyle: any; + private _tagAndTagStyle: any; + private _firstOrLastChildStyle: any; private _universalStyle: any; private _id: string | null; private _classList: any[]; @@ -55,6 +57,9 @@ class Element extends Node { private _isCustomComponent: boolean; private _inheritedStyle: object; private _target:TemplateInterface; + private _hasBefore: boolean; + private _hasAfter: boolean; + private _isOpen: boolean; protected _children: Node[]; protected _pureChildren: Element[]; @@ -104,6 +109,8 @@ class Element extends Node { this._event = {}; this._idStyle = {}; this._tagStyle = {}; + this._tagAndTagStyle = {}; + this._firstOrLastChildStyle = {}; this._universalStyle = {}; this._id = null; this._classList = []; @@ -171,6 +178,30 @@ class Element extends Node { this._vm = newVm; } + public get hasBefore() { + return this._hasBefore; + } + + public set hasBefore(hasBefore: boolean) { + this._hasBefore = hasBefore; + } + + public get hasAfter() { + return this._hasAfter; + } + + public set hasAfter(hasAfter: boolean) { + this._hasAfter = hasAfter; + } + + public get isOpen() { + return this._isOpen; + } + + public set isOpen(isOpen: boolean) { + this._isOpen = isOpen; + } + /** * Class style object of this Element, which keys is style name, and values is style values. * @type {JSON} @@ -777,7 +808,7 @@ class Element extends Node { return; } // If inline id class style has define return. - if (this.style[key] || this._idStyle[key] || this._classStyle[key]) { + if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key] || this._tagAndTagStyle[key]) { return; } this._tagStyle[key] = value; @@ -789,12 +820,46 @@ class Element extends Node { } } + public setTagAndTagStyle(key: string, value: string | number, silent: boolean = false): void { + if (this._tagAndTagStyle[key] === value && silent !== false) { + return; + } + // If inline id class style has define return. + if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key]) { + return; + } + this._tagAndTagStyle[key] = value; + const taskCenter = this.getTaskCenter(this.docId); + if (!silent && taskCenter) { + const result = {}; + result[key] = value; + taskCenter.send('dom', { action: 'updateStyle' }, [this.ref, result]); + } + } + + public setFirstOrLastChildStyle(key: string, value: string | number, silent: boolean = false): void { + if (this._firstOrLastChildStyle[key] === value && silent !== false) { + return; + } + // If inline id class style has define return. + if (this.style[key] || this._idStyle[key]) { + return; + } + this._firstOrLastChildStyle[key] = value; + const taskCenter = this.getTaskCenter(this.docId); + if (!silent && taskCenter) { + const result = {}; + result[key] = value; + taskCenter.send('dom', { action: 'updateStyle' }, [this.ref, result]); + } + } + public setUniversalStyle(key: string, value: string | number, silent: boolean = false): void { if (this._universalStyle[key] === value && silent !== false) { return; } // If inline id class style has define return. - if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._tagStyle[key]) { + if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._tagStyle[key] || this._tagAndTagStyle[key]) { return; } this._universalStyle[key] = value; @@ -881,7 +946,9 @@ class Element extends Node { const style = Object.assign({}, this._inheritedStyle); this.assignStyle(style, this._universalStyle); this.assignStyle(style, this._tagStyle); + this.assignStyle(style, this._tagAndTagStyle); this.assignStyle(style, this._classStyle); + this.assignStyle(style, this._firstOrLastChildStyle); this.assignStyle(style, this._idStyle); this.assignStyle(style, this.style); return style; @@ -983,6 +1050,8 @@ class Element extends Node { this._event = {}; this._idStyle = {}; this._tagStyle = {}; + this._tagAndTagStyle = {}; + this._firstOrLastChildStyle = {}; this._universalStyle = {}; this._classList.length = 0; From 1d9c5cb386030e152a99e2b027030e5dbb070f98 Mon Sep 17 00:00:00 2001 From: yaoyuchi Date: Tue, 18 Jan 2022 14:34:14 +0800 Subject: [PATCH 2/4] suport css selector Signed-off-by: yaoyuchi --- runtime/main/model/directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/main/model/directive.ts b/runtime/main/model/directive.ts index f3f221c1..a98739e9 100644 --- a/runtime/main/model/directive.ts +++ b/runtime/main/model/directive.ts @@ -835,7 +835,7 @@ export function bindDir(vm: Vm, el: Element, name: string, data: object, isFirst el.idStyle[id] = ''; } } - if (name === 'tagStyle' || name === 'tagAndIdStyle') { // ??style keys??DD??D¨°¡ê???content::before???¨²content::after???¡ă + if (name === 'tagStyle' || name === 'tagAndIdStyle') { let j: number = 0; let k: number = 0; let temp: string = null; From 3aa855671b76117a9cf5b79dd97ca3ece10fed27 Mon Sep 17 00:00:00 2001 From: yaoyuchi Date: Wed, 19 Jan 2022 18:09:53 +0800 Subject: [PATCH 3/4] support css selector Signed-off-by: yaoyuchi --- runtime/main/model/compiler.ts | 27 ++++---- runtime/main/model/directive.ts | 110 +++++++++++++++++++------------- runtime/vdom/Element.ts | 27 +++++++- 3 files changed, 103 insertions(+), 61 deletions(-) diff --git a/runtime/main/model/compiler.ts b/runtime/main/model/compiler.ts index f86355a0..00c55ae7 100644 --- a/runtime/main/model/compiler.ts +++ b/runtime/main/model/compiler.ts @@ -199,11 +199,11 @@ function compileVm(vm: Vm, body: Node): void { node.children.forEach((child: Node) => { const el = child as Element; const tag = child.type; - if (count === 0) { - setTagStyle(vm, el, tag, true, false, false); - } else if (count === node.children.length - 1) { - setTagStyle(vm, el, tag, false, true, false); - } + if (count === 0) { + setTagStyle(vm, el, tag, true, false, false); + } else if (count === node.children.length - 1) { + setTagStyle(vm, el, tag, false, true, false); + } count++; compileVm(vm, child); }); @@ -218,16 +218,16 @@ function compileCounter(vm: Vm, body: Node): void { node.children.forEach((child: Node) => { const el = child as Element; const tag = child.type; - if (count[tag] == undefined) { + if (count[tag] === undefined) { count[tag] = 1; } else { count[tag] = count[tag] + 1; } - let css = vm._css || {}; + const css = vm._css || {}; if (css) { - let data = css[tag] || {}; - if (data){ - let counterIncrement = data['counterIncrement']; + const data = css[tag] || {}; + if (data) { + const counterIncrement = data['counterIncrement']; if (counterIncrement !== undefined) { updateTagCounter(el, count[tag]); } @@ -244,10 +244,10 @@ function compileElementAndElement(vm: Vm, body: Node): void { node.children.forEach((child: Node) => { if (child.nextSibling) { const el = child.nextSibling as Element; - const tag = child.type + '+' + child.nextSibling.type + const tag = child.type + '+' + child.nextSibling.type; setTagStyle(vm, el, tag, false, false, false); } - compileElementAndElement(vm, child) + compileElementAndElement(vm, child); }); } } @@ -636,7 +636,6 @@ function compileNativeComponent(vm: Vm, template: TemplateInterface, dest: FragB if (!vm._rootEl) { vm._rootEl = element; - // Bind event earlier because of lifecycle issues. const binding: any = vm._externalBinding || {}; const target = binding.template; @@ -753,7 +752,6 @@ function bindRepeat(vm: Vm, target: TemplateInterface, fragBlock: FragBlockInter } trackMap[key] = item; }); - // Remove unused element foreach old item. const reusedList: any[] = []; const cacheList: any[] = []; @@ -773,7 +771,6 @@ function bindRepeat(vm: Vm, target: TemplateInterface, fragBlock: FragBlockInter }); } }); - // Create new element for each new item. children.length = 0; vms.length = 0; diff --git a/runtime/main/model/directive.ts b/runtime/main/model/directive.ts index a98739e9..a0cf9dd4 100644 --- a/runtime/main/model/directive.ts +++ b/runtime/main/model/directive.ts @@ -48,7 +48,6 @@ import { } from './compiler'; import Vm from './index'; import Element from '../../vdom/Element'; -import Node from '../../vdom/Node'; const SETTERS = { attr: 'setAttr', @@ -58,12 +57,13 @@ const SETTERS = { event: 'addEvent', idStyle: 'setIdStyle', tagStyle: 'setTagStyle', + attrStyle: 'setAttrStyle', tagAndTagStyle: 'setTagAndTagStyle', tagAndIdStyle: 'setTagAndIdStyle', - universalStyle: 'setUniversalStyle' + universalStyle: 'setUniversalStyle', + firstOrLastChildStyle: 'setFirstOrLastChildStyle' }; - enum ContentType {Content_String, Content_Open_Quote, Content_Close_Quote, Content_Attr, Content_Counter}; interface ContentObject { value: string, @@ -849,7 +849,6 @@ export function bindDir(vm: Vm, el: Element, name: string, data: object, isFirst } } } - let isOpen = false; while (i--) { let key = keys[i]; const value = data[key]; @@ -877,12 +876,16 @@ export function bindDir(vm: Vm, el: Element, name: string, data: object, isFirst } if (name === 'tagAndIdStyle') { - let newValue = updateTagAndIdStyle(el, key, value); + const newValue = updateTagAndIdStyle(el, key, value); methodName = SETTERS['attr']; method = el[methodName]; method.call(el, 'value', newValue); continue; } + if (name === 'attr') { + setAttributeStyle(vm, el); + } + method = el[methodName]; if (key === 'ref') { vm.$refs[value] = el; @@ -993,10 +996,10 @@ function isArray(params: any): params is Array { function splitItems(valueStr: string): void { let i: number; - let item: string = ''; + let item: string = ''; let startQuote: boolean = false; let itemList: string[] = []; - let len = valueStr.length; + const len = valueStr.length; for (i = 0; i < len; i++) { if (!startQuote) { if (valueStr[i] === '"') { @@ -1010,7 +1013,7 @@ function splitItems(valueStr: string): void { continue; } else { item = item + valueStr[i]; - if (i == len - 1) { + if (i === len - 1) { const itemListLength = itemList.length; itemList[itemListLength] = item; } @@ -1035,17 +1038,16 @@ function splitItems(valueStr: string): void { function doSplitItem(itemList: string[]): void { let i: number; - let isValidate: boolean = true; let itemListLength = itemList.length; for (i = 0; i < itemListLength; i++ ) { let item = itemList[i].trim(); if (item.indexOf('"') === 0) { item = item.replace('"', ''); item = item.replace('"', ''); - let contentObject: ContentObject = { + const contentObject: ContentObject = { value: item, contentType: ContentType.Content_String - } + }; const finallyItemsLength = finallyItems.length; finallyItems[finallyItemsLength] = contentObject; } else { @@ -1057,55 +1059,47 @@ function doSplitItem(itemList: string[]): void { } } } - if (finallyItems.length > 0) { - let i: number; - const finallyItemsLength = finallyItems.length; - for (i = 0;i < finallyItemsLength; i++) { - } - } } function splitItem(item: string): void{ - if (item.length == 0) { + if (item.length === 0) { return; } let finallyItemsLength = finallyItems.length; if (item.indexOf('open-quote') === 0) { - let subItem = item.substr(0, 10); - let contentObject: ContentObject = { + const subItem = item.substr(0, 10); + const contentObject: ContentObject = { value: subItem, contentType: ContentType.Content_Open_Quote - } - + }; finallyItems[finallyItemsLength] = contentObject; splitItem(item.substr(10).trim()); } else if (item.indexOf('close-quote') === 0) { - let subItem = item.substr(0, 11); - let contentObject: ContentObject = { + const subItem = item.substr(0, 11); + const contentObject: ContentObject = { value: subItem, contentType: ContentType.Content_Close_Quote - } + }; finallyItems[finallyItemsLength] = contentObject; splitItem(item.substr(11).trim()); } else if (item.indexOf('attr') === 0) { - let fromIndex = item.indexOf('('); - let toIndex = item.indexOf(')'); - let subLen = toIndex - fromIndex - 1; - let subItem = item.substr(fromIndex + 1, subLen).trim(); - let contentObject: ContentObject = { + const fromIndex = item.indexOf('('); + const toIndex = item.indexOf(')'); + const subLen = toIndex - fromIndex - 1; + const subItem = item.substr(fromIndex + 1, subLen).trim(); + const contentObject: ContentObject = { value: subItem, contentType: ContentType.Content_Attr - } + }; finallyItems[finallyItemsLength] = contentObject; splitItem(item.substr(toIndex + 1).trim()); } else if (item.indexOf('counter') === 0) { - let fromIndex = item.indexOf('('); - let toIndex = item.indexOf(')'); - let subItem = 'counter(0)'; - let contentObject: ContentObject = { + const toIndex = item.indexOf(')'); + const subItem = 'counter(0)'; + const contentObject: ContentObject = { value: subItem, contentType: ContentType.Content_Counter - } + }; finallyItems[finallyItemsLength] = contentObject; splitItem(item.substr(toIndex + 1).trim()); } else { @@ -1114,7 +1108,7 @@ function splitItem(item: string): void{ } function setContent(el: Element, key: string): string { - let itemLength = finallyItems.length; + const itemLength = finallyItems.length; let contentValue = ''; let newValue = ''; if (itemLength > 0) { @@ -1140,7 +1134,6 @@ function setContent(el: Element, key: string): string { } } const oldValue = el.attr['value']; - if (key === 'content::before') { newValue = contentValue + oldValue; } else if (key === 'content::after') { @@ -1197,8 +1190,8 @@ export function updateTagCounter(el: Element, counter: number): void { let fromIndex = value.indexOf('counter'); if (fromIndex !== -1) { value = value.replace('counter(0)', counter); - let methodName = SETTERS['attr']; - let method = el[methodName]; + const methodName = SETTERS['attr']; + const method = el[methodName]; method.call(el, 'value', value); } } @@ -1218,9 +1211,9 @@ function updateTagAndIdStyle(el: Element, key: string, value: string): string { newValue = contentValue + oldValue; oldValue = newValue; } else if (el.hasBefore && value === 'close-quote' && key === 'content::before') { - el.hasBefore = false; - oldValue = oldValue.substr(1, oldValue.length); - newValue = oldValue; + el.hasBefore = false; + oldValue = oldValue.substr(1, oldValue.length); + newValue = oldValue; } } } else if (key === 'content::after') { @@ -1255,4 +1248,35 @@ function updateTagAndIdStyle(el: Element, key: string, value: string): string { } } return newValue; +} + +function setAttributeStyle(vm: Vm, el: Element): void { + const css = vm._css; + if (css) { + const keys = Object.keys(css); + if (keys !== undefined || keys !== null) { + const i = keys.length; + let j: number = 0; + for (j; j < i; j++) { + let cssKey = keys[j].trim(); + if (cssKey.indexOf('[') === 0) { + cssKey = cssKey.substr(1, cssKey.length - 2); + let equalIndex = cssKey.indexOf('='); + if (equalIndex !== -1) { + const attrId = cssKey.substr(0, equalIndex).trim(); + let attrValue = cssKey.substr(equalIndex + 1).trim(); + if (attrValue.indexOf('\"') !== -1) { + attrValue = attrValue.replace('"', '').trim(); + attrValue = attrValue.replace('"', '').trim(); + } + const elValue = el.attr[attrId]; + if (elValue !== undefined && elValue === attrValue) { + let newKey = keys[j]; + bindDir(vm, el, 'attrStyle', css[newKey]); + } + } + } + } + } + } } \ No newline at end of file diff --git a/runtime/vdom/Element.ts b/runtime/vdom/Element.ts index f1773dc8..6ca28f43 100644 --- a/runtime/vdom/Element.ts +++ b/runtime/vdom/Element.ts @@ -47,6 +47,7 @@ class Element extends Node { private _event: any; private _idStyle: any; private _tagStyle: any; + private _attrStyle: any; private _tagAndTagStyle: any; private _firstOrLastChildStyle: any; private _universalStyle: any; @@ -109,6 +110,7 @@ class Element extends Node { this._event = {}; this._idStyle = {}; this._tagStyle = {}; + this._attrStyle = {}; this._tagAndTagStyle = {}; this._firstOrLastChildStyle = {}; this._universalStyle = {}; @@ -808,7 +810,7 @@ class Element extends Node { return; } // If inline id class style has define return. - if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key] || this._tagAndTagStyle[key]) { + if (this.style[key] || this._idStyle[key] || this._attrStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key] || this._tagAndTagStyle[key]) { return; } this._tagStyle[key] = value; @@ -820,12 +822,29 @@ class Element extends Node { } } + public setAttrStyle(key: string, value: string | number, silent: boolean = false): void { + if (this._attrStyle[key] === value && silent !== false) { + return; + } + // If inline id style define return. + if (this.style[key] || this._idStyle[key]) { + return; + } + this._attrStyle[key] = value; + const taskCenter = this.getTaskCenter(this.docId); + if (!silent && taskCenter) { + const result = {}; + result[key] = value; + taskCenter.send('dom', { action: 'updateStyle' }, [this.ref, result]); + } + } + public setTagAndTagStyle(key: string, value: string | number, silent: boolean = false): void { if (this._tagAndTagStyle[key] === value && silent !== false) { return; } // If inline id class style has define return. - if (this.style[key] || this._idStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key]) { + if (this.style[key] || this._idStyle[key] || this._attrStyle[key] || this._classStyle[key] || this._firstOrLastChildStyle[key]) { return; } this._tagAndTagStyle[key] = value; @@ -842,7 +861,7 @@ class Element extends Node { return; } // If inline id class style has define return. - if (this.style[key] || this._idStyle[key]) { + if (this.style[key] || this._idStyle[key] || this._attrStyle[key]) { return; } this._firstOrLastChildStyle[key] = value; @@ -948,6 +967,7 @@ class Element extends Node { this.assignStyle(style, this._tagStyle); this.assignStyle(style, this._tagAndTagStyle); this.assignStyle(style, this._classStyle); + this.assignStyle(style, this._attrStyle); this.assignStyle(style, this._firstOrLastChildStyle); this.assignStyle(style, this._idStyle); this.assignStyle(style, this.style); @@ -1050,6 +1070,7 @@ class Element extends Node { this._event = {}; this._idStyle = {}; this._tagStyle = {}; + this._attrStyle = {}; this._tagAndTagStyle = {}; this._firstOrLastChildStyle = {}; this._universalStyle = {}; From c3c1b1b693432985d8c78f8cd692affa6e9d3b84 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jan 2022 10:22:56 +0800 Subject: [PATCH 4/4] support css selector 3568 Signed-off-by: yaoyuchi --- runtime/main/model/compiler.ts | 87 ++++++++++++++++++++++++++++++++- runtime/main/model/directive.ts | 6 +-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/runtime/main/model/compiler.ts b/runtime/main/model/compiler.ts index 00c55ae7..e445895f 100644 --- a/runtime/main/model/compiler.ts +++ b/runtime/main/model/compiler.ts @@ -42,7 +42,8 @@ import { bindSubVmAfterInitialized, newWatch, bindDir, - updateTagCounter + updateTagCounter, + setAttributeStyle } from './directive'; import { createBlock, @@ -133,6 +134,7 @@ export function build(vm: Vm) { compileVm(vm, body); compileCounter(vm, body); compileElementAndElement(vm, body); + compileAttrStyle(vm, body); Log.debug(`"OnReady" lifecycle in Vm(${vm._type}).`); vm.$emit('hook:onReady'); if (vm._parent) { @@ -193,6 +195,25 @@ function compile(vm: Vm, target: TemplateInterface, dest: FragBlockInterface | E } function compileVm(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + let count = 0; + node.children.forEach((child: Node) => { + const el = child as Element; + const tag = child.type; + if (count === 0) { + setTagStyle(vm, el, tag, true, false, false); + } else if (count === node.children.length - 1) { + setTagStyle(vm, el, tag, false, true, false); + } + count++; + compileVmChild(vm, child); + + }); + } +} + +function compileVmChild(vm: Vm, body: Node): void { if (body.nodeType === Node.NodeType.Element) { const node: Element = body as Element; let count = 0; @@ -211,6 +232,34 @@ function compileVm(vm: Vm, body: Node): void { } function compileCounter(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + let count = {}; + + node.children.forEach((child: Node) => { + const el = child as Element; + const tag = child.type; + if (count[tag] === undefined) { + count[tag] = 1; + } else { + count[tag] = count[tag] + 1; + } + const css = vm._css || {}; + if (css) { + const data = css[tag] || {}; + if (data) { + const counterIncrement = data['counterIncrement']; + if (counterIncrement !== undefined) { + updateTagCounter(el, count[tag]); + } + } + } + compileCounterChild(vm, child); + }); + } +} + +function compileCounterChild(vm: Vm, body: Node): void { if (body.nodeType === Node.NodeType.Element) { const node: Element = body as Element; let count = {}; @@ -238,7 +287,43 @@ function compileCounter(vm: Vm, body: Node): void { } } +function compileAttrStyle(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + node.children.forEach((child: Node) => { + const el = child as Element; + setAttributeStyle(vm, el); + compileAttrStyleChild(vm, child); + }); + } +} + +function compileAttrStyleChild(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + node.children.forEach((child: Node) => { + const el = child as Element; + setAttributeStyle(vm, el); + compileAttrStyle(vm, child); + }); + } +} + function compileElementAndElement(vm: Vm, body: Node): void { + if (body.nodeType === Node.NodeType.Element) { + const node: Element = body as Element; + node.children.forEach((child: Node) => { + if (child.nextSibling) { + const el = child.nextSibling as Element; + const tag = child.type + '+' + child.nextSibling.type; + setTagStyle(vm, el, tag, false, false, false); + } + compileElementAndElementChild(vm, child); + }); + } +} + +function compileElementAndElementChild(vm: Vm, body: Node): void { if (body.nodeType === Node.NodeType.Element) { const node: Element = body as Element; node.children.forEach((child: Node) => { diff --git a/runtime/main/model/directive.ts b/runtime/main/model/directive.ts index a0cf9dd4..2b03ee7a 100644 --- a/runtime/main/model/directive.ts +++ b/runtime/main/model/directive.ts @@ -882,10 +882,6 @@ export function bindDir(vm: Vm, el: Element, name: string, data: object, isFirst method.call(el, 'value', newValue); continue; } - if (name === 'attr') { - setAttributeStyle(vm, el); - } - method = el[methodName]; if (key === 'ref') { vm.$refs[value] = el; @@ -1250,7 +1246,7 @@ function updateTagAndIdStyle(el: Element, key: string, value: string): string { return newValue; } -function setAttributeStyle(vm: Vm, el: Element): void { +export function setAttributeStyle(vm: Vm, el: Element): void { const css = vm._css; if (css) { const keys = Object.keys(css);