提交 ecb06af0 编写于 作者: fxy060608's avatar fxy060608

wip: vdom

上级 2a227a9a
......@@ -6362,6 +6362,7 @@ const props$9 = /* @__PURE__ */ shared.extend({}, props$o, {
default: ""
}
});
let fixMargin = false;
var index$c = /* @__PURE__ */ defineBuiltInComponent({
name: "Textarea",
props: props$9,
......@@ -6426,8 +6427,6 @@ var index$c = /* @__PURE__ */ defineBuiltInComponent({
textarea.blur();
}
}
const DARK_TEST_STRING = "(prefers-color-scheme: dark)";
const fixMargin = String(navigator.platform).indexOf("iP") === 0 && String(navigator.vendor).indexOf("Apple") === 0 && window.matchMedia(DARK_TEST_STRING).media !== DARK_TEST_STRING;
return () => {
let textareaNode = props2.disabled && fixDisabledColor ? vue.createVNode("textarea", {
"ref": fieldRef,
......@@ -8986,16 +8985,29 @@ function usePickerState(props2) {
rangeArray
};
}
function useIsiPad() {
const isiPad = vue.ref(false);
return isiPad;
}
function useSystem() {
const _system = vue.ref("");
return _system;
}
let __contentVisibleDelay;
function usePickerMethods(props2, state, trigger, rootRef, pickerRef, selectRef, inputRef) {
const isiPad = useIsiPad();
const _system = useSystem();
const selectorTypeComputed = vue.computed(() => {
const type = props2.selectorType;
if (Object.values(selectorType).includes(type)) {
return type;
}
return String(navigator.vendor).indexOf("Apple") === 0 && navigator.maxTouchPoints > 0 ? selectorType.PICKER : selectorType.SELECT;
return isiPad.value ? selectorType.PICKER : selectorType.SELECT;
});
const system = vue.computed(() => {
if (props2.mode === mode.DATE && !Object.values(fields).includes(props2.fields) && state.isDesktop) {
return _system.value;
}
return "";
});
const startArray = vue.computed(() => {
......
......@@ -12675,6 +12675,11 @@ const props$g = /* @__PURE__ */ extend({}, props$v, {
default: ""
}
});
let fixMargin = false;
function setFixMargin() {
const DARK_TEST_STRING = "(prefers-color-scheme: dark)";
fixMargin = String(navigator.platform).indexOf("iP") === 0 && String(navigator.vendor).indexOf("Apple") === 0 && window.matchMedia(DARK_TEST_STRING).media !== DARK_TEST_STRING;
}
var index$c = /* @__PURE__ */ defineBuiltInComponent({
name: "Textarea",
props: props$g,
......@@ -12739,8 +12744,9 @@ var index$c = /* @__PURE__ */ defineBuiltInComponent({
textarea.blur();
}
}
const DARK_TEST_STRING = "(prefers-color-scheme: dark)";
const fixMargin = String(navigator.platform).indexOf("iP") === 0 && String(navigator.vendor).indexOf("Apple") === 0 && window.matchMedia(DARK_TEST_STRING).media !== DARK_TEST_STRING;
{
setFixMargin();
}
return () => {
let textareaNode = props2.disabled && fixDisabledColor ? createVNode("textarea", {
"ref": fieldRef,
......@@ -19710,22 +19716,45 @@ function usePickerState(props2) {
rangeArray
};
}
const getiPadFlag = () => String(navigator.vendor).indexOf("Apple") === 0 && navigator.maxTouchPoints > 0;
function useIsiPad() {
const isiPad = ref(false);
{
isiPad.value = getiPadFlag();
}
return isiPad;
}
const getSystem = () => {
if (/win|mac/i.test(navigator.platform)) {
if (navigator.vendor === "Google Inc.") {
return "chrome";
} else if (/Firefox/.test(navigator.userAgent)) {
return "firefox";
}
}
return "";
};
function useSystem() {
const _system = ref("");
{
_system.value = getSystem();
}
return _system;
}
let __contentVisibleDelay;
function usePickerMethods(props2, state2, trigger, rootRef, pickerRef, selectRef, inputRef) {
const isiPad = useIsiPad();
const _system = useSystem();
const selectorTypeComputed = computed(() => {
const type = props2.selectorType;
if (Object.values(selectorType).includes(type)) {
return type;
}
return String(navigator.vendor).indexOf("Apple") === 0 && navigator.maxTouchPoints > 0 ? selectorType.PICKER : selectorType.SELECT;
return isiPad.value ? selectorType.PICKER : selectorType.SELECT;
});
const system = computed(() => {
if (props2.mode === mode.DATE && !Object.values(fields).includes(props2.fields) && state2.isDesktop && /win|mac/i.test(navigator.platform)) {
if (navigator.vendor === "Google Inc.") {
return "chrome";
} else if (/Firefox/.test(navigator.userAgent)) {
return "firefox";
}
if (props2.mode === mode.DATE && !Object.values(fields).includes(props2.fields) && state2.isDesktop) {
return _system.value;
}
return "";
});
......
......@@ -3,7 +3,7 @@ import { proxyStyle, UniCSSStyleDeclaration } from '../../src/vdom/Style'
describe('vdom', () => {
test('style', () => {
const uniCSSStyle = proxyStyle(new UniCSSStyleDeclaration())
expect(uniCSSStyle.toJSON()).toBe(null)
expect(uniCSSStyle.toJSON()).toBeUndefined()
uniCSSStyle.cssText = 'color:red'
expect(uniCSSStyle.toJSON()).toBe(uniCSSStyle.cssText)
uniCSSStyle.backgroundColor = 'black'
......
import {
COMPONENT_MAP,
encodeTag,
decodeTag,
ATTR_MAP,
encodeAttr,
decodeAttr,
} from '../../src/vdom/utils'
describe('encode', () => {
test('tag', () => {
Object.keys(COMPONENT_MAP).forEach((tag) => {
const encodedTag = encodeTag(tag)
expect(decodeTag(encodedTag)).toBe(tag.toLowerCase())
})
const encodedTag = encodeTag('unicloud-db')
expect(decodeTag(encodedTag)).toBe(encodedTag)
})
test('attr', () => {
Object.keys(ATTR_MAP).forEach((name) => {
const encodedAttr = encodeAttr(name)
expect(decodeAttr(encodedAttr)).toBe(name)
})
const encodedAttr = encodeAttr('id')
expect(decodeAttr(encodedAttr)).toBe(encodedAttr)
})
})
......@@ -329,7 +329,12 @@ class UniCSSStyleDeclaration {
if (hasCssText && hasValue) {
return [_cssText, _value];
}
return hasCssText ? _cssText : _value;
if (hasCssText) {
return _cssText;
}
if (hasValue) {
return _value;
}
}
}
const STYLE_PROPS = [
......@@ -359,6 +364,118 @@ function proxyStyle(uniCssStyle) {
});
}
const ATTR_MAP = {
class: '.c',
style: '.s',
};
function encodeAttr(name) {
return ATTR_MAP[name] || name;
}
const ATTR_RESTORE_MAP = {
'.c': 'class',
'.s': 'style',
};
function decodeAttr(name) {
return ATTR_RESTORE_MAP[name] || name;
}
const COMPONENT_MAP = {
VIEW: 1,
IMAGE: 2,
TEXT: 3,
'#text': 4,
'#comment': 5,
NAVIGATOR: 6,
FORM: 7,
BUTTON: 8,
INPUT: 9,
LABEL: 10,
RADIO: 11,
CHECKBOX: 12,
'CHECKBOX-GROUP': 13,
AD: 14,
AUDIO: 15,
CAMERA: 16,
CANVAS: 17,
'COVER-IMAGE': 18,
'COVER-VIEW': 19,
EDITOR: 20,
'FUNCTIONAL-PAGE-NAVIGATOR': 21,
ICON: 22,
'RADIO-GROUP': 23,
'LIVE-PLAYER': 24,
'LIVE-PUSHER': 25,
MAP: 26,
'MOVABLE-AREA': 27,
'MOVABLE-VIEW': 28,
'OFFICIAL-ACCOUNT': 29,
'OPEN-DATA': 30,
PICKER: 31,
'PICKER-VIEW': 32,
'PICKER-VIEW-COLUMN': 33,
PROGRESS: 34,
'RICH-TEXT': 35,
'SCROLL-VIEW': 36,
SLIDER: 37,
SWIPER: 38,
'SWIPER-ITEM': 39,
SWITCH: 40,
TEXTAREA: 41,
VIDEO: 42,
'WEB-VIEW': 43,
};
function encodeTag(tag) {
return COMPONENT_MAP[tag] || tag;
}
const COMPONENT_ARR = [
'',
'view',
'image',
'text',
'#text',
'#comment',
'navigator',
'form',
'button',
'input',
'label',
'radio',
'checkbox',
'checkbox-group',
'ad',
'audio',
'camera',
'canvas',
'cover-image',
'cover-view',
'editor',
'functional-page-navigator',
'icon',
'radio-group',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'textarea',
'video',
'web-view',
];
function decodeTag(tag) {
return COMPONENT_ARR[tag] || tag;
}
const NODE_TYPE_PAGE = 0;
const NODE_TYPE_ELEMENT = 1;
const NODE_TYPE_TEXT = 3;
......@@ -382,12 +499,19 @@ function checkNodeId(node) {
node.nodeId = node.pageNode.genId();
}
}
// 为优化性能,各平台不使用proxy来实现node的操作拦截,而是直接通过pageNode定制
class UniNode extends UniEventTarget {
constructor(nodeType, nodeName) {
constructor(nodeType, nodeName, container) {
super();
this.pageNode = null;
this.parentNode = null;
this._text = null;
if (container) {
const { pageNode } = container;
this.pageNode = pageNode;
this.nodeId = pageNode.genId();
pageNode.onCreate(this, encodeTag(nodeName));
}
this.nodeType = nodeType;
this.nodeName = nodeName;
this.childNodes = [];
......@@ -412,6 +536,9 @@ class UniNode extends UniEventTarget {
}
set textContent(text) {
this._text = text;
if (this.pageNode) {
this.pageNode.onTextContent(this, text);
}
}
get parentElement() {
const { parentNode } = this;
......@@ -443,17 +570,21 @@ class UniNode extends UniEventTarget {
newChild.parentNode = this;
checkNodeId(newChild);
const { childNodes } = this;
let index;
if (refChild) {
const index = childNodes.indexOf(refChild);
index = childNodes.indexOf(refChild);
if (index === -1) {
throw new DOMException(`Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`);
}
childNodes.splice(childNodes.indexOf(refChild), 0, newChild);
childNodes.splice(index, 0, newChild);
}
else {
index = childNodes.length;
childNodes.push(newChild);
}
return newChild;
return this.pageNode
? this.pageNode.onInsertBefore(this, newChild, index)
: newChild;
}
removeChild(oldChild) {
const { childNodes } = this;
......@@ -463,12 +594,14 @@ class UniNode extends UniEventTarget {
}
oldChild.parentNode = null;
childNodes.splice(index, 1);
return oldChild;
return this.pageNode
? this.pageNode.onRemoveChild(this, oldChild)
: oldChild;
}
}
class UniBaseNode extends UniNode {
constructor(nodeType, nodeName) {
super(nodeType, nodeName);
constructor(nodeType, nodeName, container) {
super(nodeType, nodeName, container);
this.attributes = Object.create(null);
this._html = null;
this.style = proxyStyle(new UniCSSStyleDeclaration());
......@@ -500,21 +633,31 @@ class UniBaseNode extends UniNode {
}
}
getAttribute(qualifiedName) {
return this.attributes[qualifiedName];
return this.attributes[encodeAttr(qualifiedName)];
}
removeAttribute(qualifiedName) {
qualifiedName = encodeAttr(qualifiedName);
delete this.attributes[qualifiedName];
if (this.pageNode) {
this.pageNode.onRemoveAttribute(this, qualifiedName);
}
}
setAttribute(qualifiedName, value) {
qualifiedName = encodeAttr(qualifiedName);
this.attributes[qualifiedName] = value;
if (this.pageNode) {
this.pageNode.onSetAttribute(this, qualifiedName, value);
}
}
toJSON() {
toJSON(opts = {}) {
const res = {
i: this.nodeId,
n: this.nodeName,
a: this.attributes,
s: this.style.toJSON(),
};
if (!opts.attr) {
res.i = this.nodeId;
res.n = encodeTag(this.nodeName);
}
if (this._text !== null) {
res.t = this._text;
}
......@@ -523,15 +666,23 @@ class UniBaseNode extends UniNode {
}
class UniCommentNode extends UniNode {
constructor(text) {
super(NODE_TYPE_COMMENT, '#comment');
constructor(text, container) {
super(NODE_TYPE_COMMENT, '#comment', container);
this._text = text;
}
toJSON(opts = {}) {
return opts.attr
? { t: this._text }
: {
i: this.nodeId,
t: this._text,
};
}
}
class UniElement extends UniBaseNode {
constructor(nodeName) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase());
constructor(nodeName, container) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase(), container);
this.tagName = this.nodeName;
}
}
......@@ -547,8 +698,8 @@ class UniTextAreaElement extends UniInputElement {
}
class UniTextNode extends UniBaseNode {
constructor(text) {
super(NODE_TYPE_TEXT, '#text');
constructor(text, container) {
super(NODE_TYPE_TEXT, '#text', container);
this._text = text;
}
get nodeValue() {
......@@ -556,6 +707,9 @@ class UniTextNode extends UniBaseNode {
}
set nodeValue(text) {
this._text = text;
if (this.pageNode) {
this.pageNode.onNodeValue(this, text);
}
}
}
......@@ -770,8 +924,12 @@ exports.callOptions = callOptions;
exports.createRpx2Unit = createRpx2Unit;
exports.debounce = debounce;
exports.decode = decode;
exports.decodeAttr = decodeAttr;
exports.decodeTag = decodeTag;
exports.decodedQuery = decodedQuery;
exports.defaultRpx2Unit = defaultRpx2Unit;
exports.encodeAttr = encodeAttr;
exports.encodeTag = encodeTag;
exports.formatDateTime = formatDateTime;
exports.getCustomDataset = getCustomDataset;
exports.getEnvLocale = getEnvLocale;
......
......@@ -33,14 +33,22 @@ export declare function debounce(fn: Function, delay: number): {
*/
export declare function decode(text: string | number): string;
export declare function decodeAttr(name: string): string;
export declare function decodedQuery(query?: Record<string, any>): Record<string, string>;
export declare function decodeTag(tag: string | number): string | number;
export declare const defaultRpx2Unit: {
unit: string;
unitRatio: number;
unitPrecision: number;
};
export declare function encodeAttr(name: string): string;
export declare function encodeTag(tag: string): string | number;
export declare function formatDateTime({ date, mode }: {
date?: Date | undefined;
mode?: string | undefined;
......@@ -68,8 +76,16 @@ export declare function isNativeTag(tag: string): boolean;
export declare interface IUniPageNode {
pageId: number;
pageNode: IUniPageNode | null;
genId: () => number;
push: (...args: any[]) => void;
onCreate: (thisNode: UniNode, nodeName: string | number) => UniNode;
onInsertBefore: (thisNode: UniNode, newChild: UniNode, index: number) => UniNode;
onRemoveChild: (thisNode: UniNode, oldChild: UniNode) => UniNode;
onSetAttribute: (thisNode: UniNode, qualifiedName: string, value: unknown) => void;
onRemoveAttribute: (thisNode: UniNode, qualifiedName: string) => void;
onTextContent: (thisNode: UniNode, text: string) => void;
onNodeValue: (thisNode: UniNode, val: string | null) => void;
}
export declare const NAVBAR_HEIGHT = 44;
......@@ -151,7 +167,7 @@ export declare class UniBaseNode extends UniNode {
attributes: Record<string, unknown>;
style: UniCSSStyleDeclaration;
protected _html: string | null;
constructor(nodeType: UniNodeType, nodeName: string);
constructor(nodeType: UniNodeType, nodeName: string, container: UniElement | IUniPageNode);
get className(): string;
set className(val: string);
get innerHTML(): string;
......@@ -161,11 +177,23 @@ export declare class UniBaseNode extends UniNode {
getAttribute(qualifiedName: string): unknown;
removeAttribute(qualifiedName: string): void;
setAttribute(qualifiedName: string, value: unknown): void;
toJSON(): UniNodeJSON;
toJSON(opts?: {
attr?: boolean;
children?: boolean;
}): Partial<UniNodeJSON>;
}
export declare class UniCommentNode extends UniNode {
constructor(text: string);
constructor(text: string, container: UniElement | IUniPageNode);
toJSON(opts?: {
attr?: boolean;
}): {
t: string;
i?: undefined;
} | {
i: number;
t: string;
};
}
declare class UniCSSStyleDeclaration {
......@@ -177,14 +205,14 @@ declare class UniCSSStyleDeclaration {
removeProperty(property: string): string;
get cssText(): string;
set cssText(cssText: string);
toJSON(): UniCSSStyleDeclarationJSON;
toJSON(): UniCSSStyleDeclarationJSON | undefined;
}
declare type UniCSSStyleDeclarationJSON = string | null | Record<string, string | string[]> | [string, Record<string, string | string[]>];
export declare class UniElement extends UniBaseNode {
tagName: string;
constructor(nodeName: string);
constructor(nodeName: string, container: UniElement | IUniPageNode);
}
export declare class UniEvent {
......@@ -230,7 +258,7 @@ export declare class UniNode extends UniEventTarget {
pageNode: IUniPageNode | null;
parentNode: UniNode | null;
protected _text: string | null;
constructor(nodeType: UniNodeType, nodeName: string);
constructor(nodeType: UniNodeType, nodeName: string, container: UniElement | IUniPageNode);
get firstChild(): UniNode | null;
get lastChild(): UniNode | null;
get nextSibling(): UniNode | null;
......@@ -240,10 +268,10 @@ export declare class UniNode extends UniEventTarget {
set textContent(text: string);
get parentElement(): UniElement | null;
get previousSibling(): UniNode | null;
appendChild<T extends UniNode>(newChild: T): T;
appendChild(newChild: UniNode): UniNode;
cloneNode(deep?: boolean): UniNode;
insertBefore<T extends UniNode>(newChild: T, refChild: UniNode | null): T;
removeChild<T extends UniNode>(oldChild: T): T;
insertBefore(newChild: UniNode, refChild: UniNode | null): UniNode;
removeChild(oldChild: UniNode): UniNode;
}
export declare interface UniNodeJSON {
......@@ -254,7 +282,7 @@ export declare interface UniNodeJSON {
/**
* nodeName
*/
n: string;
n: string | number;
/**
* attributes
*/
......@@ -262,7 +290,7 @@ export declare interface UniNodeJSON {
/**
* style
*/
s: UniCSSStyleDeclarationJSON;
s?: UniCSSStyleDeclarationJSON;
/**
* text
*/
......@@ -275,7 +303,7 @@ export declare class UniTextAreaElement extends UniInputElement {
}
export declare class UniTextNode extends UniBaseNode {
constructor(text: string);
constructor(text: string, container: UniElement | IUniPageNode);
get nodeValue(): string;
set nodeValue(text: string);
}
......
......@@ -325,7 +325,12 @@ class UniCSSStyleDeclaration {
if (hasCssText && hasValue) {
return [_cssText, _value];
}
return hasCssText ? _cssText : _value;
if (hasCssText) {
return _cssText;
}
if (hasValue) {
return _value;
}
}
}
const STYLE_PROPS = [
......@@ -355,6 +360,118 @@ function proxyStyle(uniCssStyle) {
});
}
const ATTR_MAP = {
class: '.c',
style: '.s',
};
function encodeAttr(name) {
return ATTR_MAP[name] || name;
}
const ATTR_RESTORE_MAP = {
'.c': 'class',
'.s': 'style',
};
function decodeAttr(name) {
return ATTR_RESTORE_MAP[name] || name;
}
const COMPONENT_MAP = {
VIEW: 1,
IMAGE: 2,
TEXT: 3,
'#text': 4,
'#comment': 5,
NAVIGATOR: 6,
FORM: 7,
BUTTON: 8,
INPUT: 9,
LABEL: 10,
RADIO: 11,
CHECKBOX: 12,
'CHECKBOX-GROUP': 13,
AD: 14,
AUDIO: 15,
CAMERA: 16,
CANVAS: 17,
'COVER-IMAGE': 18,
'COVER-VIEW': 19,
EDITOR: 20,
'FUNCTIONAL-PAGE-NAVIGATOR': 21,
ICON: 22,
'RADIO-GROUP': 23,
'LIVE-PLAYER': 24,
'LIVE-PUSHER': 25,
MAP: 26,
'MOVABLE-AREA': 27,
'MOVABLE-VIEW': 28,
'OFFICIAL-ACCOUNT': 29,
'OPEN-DATA': 30,
PICKER: 31,
'PICKER-VIEW': 32,
'PICKER-VIEW-COLUMN': 33,
PROGRESS: 34,
'RICH-TEXT': 35,
'SCROLL-VIEW': 36,
SLIDER: 37,
SWIPER: 38,
'SWIPER-ITEM': 39,
SWITCH: 40,
TEXTAREA: 41,
VIDEO: 42,
'WEB-VIEW': 43,
};
function encodeTag(tag) {
return COMPONENT_MAP[tag] || tag;
}
const COMPONENT_ARR = [
'',
'view',
'image',
'text',
'#text',
'#comment',
'navigator',
'form',
'button',
'input',
'label',
'radio',
'checkbox',
'checkbox-group',
'ad',
'audio',
'camera',
'canvas',
'cover-image',
'cover-view',
'editor',
'functional-page-navigator',
'icon',
'radio-group',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'textarea',
'video',
'web-view',
];
function decodeTag(tag) {
return COMPONENT_ARR[tag] || tag;
}
const NODE_TYPE_PAGE = 0;
const NODE_TYPE_ELEMENT = 1;
const NODE_TYPE_TEXT = 3;
......@@ -378,12 +495,19 @@ function checkNodeId(node) {
node.nodeId = node.pageNode.genId();
}
}
// 为优化性能,各平台不使用proxy来实现node的操作拦截,而是直接通过pageNode定制
class UniNode extends UniEventTarget {
constructor(nodeType, nodeName) {
constructor(nodeType, nodeName, container) {
super();
this.pageNode = null;
this.parentNode = null;
this._text = null;
if (container) {
const { pageNode } = container;
this.pageNode = pageNode;
this.nodeId = pageNode.genId();
pageNode.onCreate(this, encodeTag(nodeName));
}
this.nodeType = nodeType;
this.nodeName = nodeName;
this.childNodes = [];
......@@ -408,6 +532,9 @@ class UniNode extends UniEventTarget {
}
set textContent(text) {
this._text = text;
if (this.pageNode) {
this.pageNode.onTextContent(this, text);
}
}
get parentElement() {
const { parentNode } = this;
......@@ -439,17 +566,21 @@ class UniNode extends UniEventTarget {
newChild.parentNode = this;
checkNodeId(newChild);
const { childNodes } = this;
let index;
if (refChild) {
const index = childNodes.indexOf(refChild);
index = childNodes.indexOf(refChild);
if (index === -1) {
throw new DOMException(`Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`);
}
childNodes.splice(childNodes.indexOf(refChild), 0, newChild);
childNodes.splice(index, 0, newChild);
}
else {
index = childNodes.length;
childNodes.push(newChild);
}
return newChild;
return this.pageNode
? this.pageNode.onInsertBefore(this, newChild, index)
: newChild;
}
removeChild(oldChild) {
const { childNodes } = this;
......@@ -459,12 +590,14 @@ class UniNode extends UniEventTarget {
}
oldChild.parentNode = null;
childNodes.splice(index, 1);
return oldChild;
return this.pageNode
? this.pageNode.onRemoveChild(this, oldChild)
: oldChild;
}
}
class UniBaseNode extends UniNode {
constructor(nodeType, nodeName) {
super(nodeType, nodeName);
constructor(nodeType, nodeName, container) {
super(nodeType, nodeName, container);
this.attributes = Object.create(null);
this._html = null;
this.style = proxyStyle(new UniCSSStyleDeclaration());
......@@ -496,21 +629,31 @@ class UniBaseNode extends UniNode {
}
}
getAttribute(qualifiedName) {
return this.attributes[qualifiedName];
return this.attributes[encodeAttr(qualifiedName)];
}
removeAttribute(qualifiedName) {
qualifiedName = encodeAttr(qualifiedName);
delete this.attributes[qualifiedName];
if (this.pageNode) {
this.pageNode.onRemoveAttribute(this, qualifiedName);
}
}
setAttribute(qualifiedName, value) {
qualifiedName = encodeAttr(qualifiedName);
this.attributes[qualifiedName] = value;
if (this.pageNode) {
this.pageNode.onSetAttribute(this, qualifiedName, value);
}
}
toJSON() {
toJSON(opts = {}) {
const res = {
i: this.nodeId,
n: this.nodeName,
a: this.attributes,
s: this.style.toJSON(),
};
if (!opts.attr) {
res.i = this.nodeId;
res.n = encodeTag(this.nodeName);
}
if (this._text !== null) {
res.t = this._text;
}
......@@ -519,15 +662,23 @@ class UniBaseNode extends UniNode {
}
class UniCommentNode extends UniNode {
constructor(text) {
super(NODE_TYPE_COMMENT, '#comment');
constructor(text, container) {
super(NODE_TYPE_COMMENT, '#comment', container);
this._text = text;
}
toJSON(opts = {}) {
return opts.attr
? { t: this._text }
: {
i: this.nodeId,
t: this._text,
};
}
}
class UniElement extends UniBaseNode {
constructor(nodeName) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase());
constructor(nodeName, container) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase(), container);
this.tagName = this.nodeName;
}
}
......@@ -543,8 +694,8 @@ class UniTextAreaElement extends UniInputElement {
}
class UniTextNode extends UniBaseNode {
constructor(text) {
super(NODE_TYPE_TEXT, '#text');
constructor(text, container) {
super(NODE_TYPE_TEXT, '#text', container);
this._text = text;
}
get nodeValue() {
......@@ -552,6 +703,9 @@ class UniTextNode extends UniBaseNode {
}
set nodeValue(text) {
this._text = text;
if (this.pageNode) {
this.pageNode.onNodeValue(this, text);
}
}
}
......@@ -733,4 +887,4 @@ function getEnvLocale() {
return (lang && lang.replace(/[.:].*/, '')) || 'en';
}
export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, addFont, callOptions, createRpx2Unit, debounce, decode, decodedQuery, defaultRpx2Unit, formatDateTime, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, addFont, callOptions, createRpx2Unit, debounce, decode, decodeAttr, decodeTag, decodedQuery, defaultRpx2Unit, encodeAttr, encodeTag, formatDateTime, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
import { NODE_TYPE_COMMENT, UniNode } from './Node'
import { UniElement } from './Element'
import { IUniPageNode, NODE_TYPE_COMMENT, UniNode } from './Node'
export class UniCommentNode extends UniNode {
constructor(text: string) {
super(NODE_TYPE_COMMENT, '#comment')
constructor(text: string, container: UniElement | IUniPageNode) {
super(NODE_TYPE_COMMENT, '#comment', container)
this._text = text
}
toJSON(opts: { attr?: boolean } = {}) {
return opts.attr
? { t: this._text as string }
: {
i: this.nodeId!,
t: this._text as string,
}
}
}
import { NODE_TYPE_ELEMENT, UniBaseNode } from './Node'
import { IUniPageNode, NODE_TYPE_ELEMENT, UniBaseNode } from './Node'
export class UniElement extends UniBaseNode {
tagName: string
constructor(nodeName: string) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase())
constructor(nodeName: string, container: UniElement | IUniPageNode) {
super(NODE_TYPE_ELEMENT, nodeName.toUpperCase(), container)
this.tagName = this.nodeName
}
}
......
......@@ -8,6 +8,7 @@ import {
UniCSSStyleDeclaration,
UniCSSStyleDeclarationJSON,
} from './Style'
import { encodeAttr, encodeTag } from './utils'
export const NODE_TYPE_PAGE = 0
export const NODE_TYPE_ELEMENT = 1
......@@ -44,10 +45,26 @@ function checkNodeId(node: UniNode) {
export interface IUniPageNode {
pageId: number
pageNode: IUniPageNode | null
genId: () => number
push: (...args: any[]) => void
onCreate: (thisNode: UniNode, nodeName: string | number) => UniNode
onInsertBefore: (
thisNode: UniNode,
newChild: UniNode,
index: number
) => UniNode
onRemoveChild: (thisNode: UniNode, oldChild: UniNode) => UniNode
onSetAttribute: (
thisNode: UniNode,
qualifiedName: string,
value: unknown
) => void
onRemoveAttribute: (thisNode: UniNode, qualifiedName: string) => void
onTextContent: (thisNode: UniNode, text: string) => void
onNodeValue: (thisNode: UniNode, val: string | null) => void
}
// 为优化性能,各平台不使用proxy来实现node的操作拦截,而是直接通过pageNode定制
export class UniNode extends UniEventTarget {
nodeId?: number
nodeType: UniNodeType
......@@ -59,8 +76,18 @@ export class UniNode extends UniEventTarget {
protected _text: string | null = null
constructor(nodeType: UniNodeType, nodeName: string) {
constructor(
nodeType: UniNodeType,
nodeName: string,
container: UniElement | IUniPageNode
) {
super()
if (container) {
const { pageNode } = container
this.pageNode = pageNode
this.nodeId = pageNode!.genId()
pageNode!.onCreate(this, encodeTag(nodeName))
}
this.nodeType = nodeType
this.nodeName = nodeName
this.childNodes = []
......@@ -92,6 +119,9 @@ export class UniNode extends UniEventTarget {
set textContent(text: string) {
this._text = text
if (this.pageNode) {
this.pageNode.onTextContent(this, text)
}
}
get parentElement(): UniElement | null {
......@@ -106,7 +136,7 @@ export class UniNode extends UniEventTarget {
return sibling(this, 'p')
}
appendChild<T extends UniNode>(newChild: T): T {
appendChild(newChild: UniNode): UniNode {
return this.insertBefore(newChild, null)
}
......@@ -127,27 +157,31 @@ export class UniNode extends UniEventTarget {
return cloned
}
insertBefore<T extends UniNode>(newChild: T, refChild: UniNode | null): T {
insertBefore(newChild: UniNode, refChild: UniNode | null): UniNode {
removeNode(newChild)
newChild.pageNode = this.pageNode
newChild.parentNode = this
checkNodeId(newChild)
const { childNodes } = this
let index: number
if (refChild) {
const index = childNodes.indexOf(refChild)
index = childNodes.indexOf(refChild)
if (index === -1) {
throw new DOMException(
`Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`
)
}
childNodes.splice(childNodes.indexOf(refChild), 0, newChild)
childNodes.splice(index, 0, newChild)
} else {
index = childNodes.length
childNodes.push(newChild)
}
return newChild
return this.pageNode
? this.pageNode.onInsertBefore(this, newChild, index)
: newChild
}
removeChild<T extends UniNode>(oldChild: T): T {
removeChild(oldChild: UniNode): UniNode {
const { childNodes } = this
const index = childNodes.indexOf(oldChild)
if (index === -1) {
......@@ -157,7 +191,9 @@ export class UniNode extends UniEventTarget {
}
oldChild.parentNode = null
childNodes.splice(index, 1)
return oldChild
return this.pageNode
? this.pageNode.onRemoveChild(this, oldChild)
: oldChild
}
}
......@@ -169,7 +205,7 @@ export interface UniNodeJSON {
/**
* nodeName
*/
n: string
n: string | number
/**
* attributes
*/
......@@ -177,7 +213,7 @@ export interface UniNodeJSON {
/**
* style
*/
s: UniCSSStyleDeclarationJSON
s?: UniCSSStyleDeclarationJSON
/**
* text
*/
......@@ -190,8 +226,12 @@ export class UniBaseNode extends UniNode {
protected _html: string | null = null
constructor(nodeType: UniNodeType, nodeName: string) {
super(nodeType, nodeName)
constructor(
nodeType: UniNodeType,
nodeName: string,
container: UniElement | IUniPageNode
) {
super(nodeType, nodeName, container)
this.style = proxyStyle(new UniCSSStyleDeclaration())
}
......@@ -236,24 +276,34 @@ export class UniBaseNode extends UniNode {
}
getAttribute(qualifiedName: string) {
return this.attributes[qualifiedName]
return this.attributes[encodeAttr(qualifiedName)]
}
removeAttribute(qualifiedName: string): void {
qualifiedName = encodeAttr(qualifiedName)
delete this.attributes[qualifiedName]
if (this.pageNode) {
this.pageNode.onRemoveAttribute(this, qualifiedName)
}
}
setAttribute(qualifiedName: string, value: unknown): void {
qualifiedName = encodeAttr(qualifiedName)
this.attributes[qualifiedName] = value
if (this.pageNode) {
this.pageNode.onSetAttribute(this, qualifiedName, value)
}
}
toJSON() {
const res: UniNodeJSON = {
i: this.nodeId!,
n: this.nodeName,
toJSON(opts: { attr?: boolean; children?: boolean } = {}) {
const res: Partial<UniNodeJSON> = {
a: this.attributes,
s: this.style.toJSON(),
}
if (!opts.attr) {
res.i = this.nodeId
res.n = encodeTag(this.nodeName)
}
if (this._text !== null) {
res.t = this._text
}
......
......@@ -44,14 +44,19 @@ export class UniCSSStyleDeclaration {
this._cssText = cssText
}
toJSON(): UniCSSStyleDeclarationJSON {
toJSON(): UniCSSStyleDeclarationJSON | undefined {
const { _cssText, _value } = this
const hasCssText = _cssText !== null
const hasValue = _value !== null
if (hasCssText && hasValue) {
return [_cssText!, _value!]
}
return hasCssText ? _cssText : _value
if (hasCssText) {
return _cssText
}
if (hasValue) {
return _value
}
}
}
......
import { NODE_TYPE_TEXT, UniBaseNode } from './Node'
import { UniElement } from './Element'
import { IUniPageNode, NODE_TYPE_TEXT, UniBaseNode } from './Node'
export class UniTextNode extends UniBaseNode {
constructor(text: string) {
super(NODE_TYPE_TEXT, '#text')
constructor(text: string, container: UniElement | IUniPageNode) {
super(NODE_TYPE_TEXT, '#text', container)
this._text = text
}
......@@ -12,5 +13,8 @@ export class UniTextNode extends UniBaseNode {
set nodeValue(text: string) {
this._text = text
if (this.pageNode) {
this.pageNode.onNodeValue(this, text)
}
}
}
......@@ -12,3 +12,4 @@ export {
IUniPageNode,
} from './Node'
export { UniTextNode } from './Text'
export { encodeTag, decodeTag, encodeAttr, decodeAttr } from './utils'
export const ATTR_MAP = {
class: '.c',
style: '.s',
}
export function encodeAttr(name: string) {
return ATTR_MAP[name as keyof typeof ATTR_MAP] || name
}
export const ATTR_RESTORE_MAP = {
'.c': 'class',
'.s': 'style',
}
export function decodeAttr(name: string) {
return ATTR_RESTORE_MAP[name as keyof typeof ATTR_RESTORE_MAP] || name
}
export const COMPONENT_MAP = {
VIEW: 1,
IMAGE: 2,
TEXT: 3,
'#text': 4,
'#comment': 5,
NAVIGATOR: 6,
FORM: 7,
BUTTON: 8,
INPUT: 9,
LABEL: 10,
RADIO: 11,
CHECKBOX: 12,
'CHECKBOX-GROUP': 13,
AD: 14,
AUDIO: 15,
CAMERA: 16,
CANVAS: 17,
'COVER-IMAGE': 18,
'COVER-VIEW': 19,
EDITOR: 20,
'FUNCTIONAL-PAGE-NAVIGATOR': 21,
ICON: 22,
'RADIO-GROUP': 23,
'LIVE-PLAYER': 24,
'LIVE-PUSHER': 25,
MAP: 26,
'MOVABLE-AREA': 27,
'MOVABLE-VIEW': 28,
'OFFICIAL-ACCOUNT': 29,
'OPEN-DATA': 30,
PICKER: 31,
'PICKER-VIEW': 32,
'PICKER-VIEW-COLUMN': 33,
PROGRESS: 34,
'RICH-TEXT': 35,
'SCROLL-VIEW': 36,
SLIDER: 37,
SWIPER: 38,
'SWIPER-ITEM': 39,
SWITCH: 40,
TEXTAREA: 41,
VIDEO: 42,
'WEB-VIEW': 43,
}
export function encodeTag(tag: string) {
return COMPONENT_MAP[tag as keyof typeof COMPONENT_MAP] || tag
}
const COMPONENT_ARR = [
'',
'view',
'image',
'text',
'#text',
'#comment',
'navigator',
'form',
'button',
'input',
'label',
'radio',
'checkbox',
'checkbox-group',
'ad',
'audio',
'camera',
'canvas',
'cover-image',
'cover-view',
'editor',
'functional-page-navigator',
'icon',
'radio-group',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'textarea',
'video',
'web-view',
]
export function decodeTag(tag: string | number) {
return COMPONENT_ARR[tag as number] || tag
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册