refactor: ♻️ type infer
This commit is contained in:
@ -1,63 +1,88 @@
|
||||
import { DomUtil } from '../util/dom'
|
||||
import { Global } from '../global'
|
||||
import type { Svg } from './container/svg'
|
||||
import type { Dom } from './dom'
|
||||
import { Registry } from './registry'
|
||||
import { Base } from './base'
|
||||
import { ElementMapping, HTMLTagMapping, SVGTagMapping } from './types'
|
||||
|
||||
export namespace Adopter {
|
||||
const cache: WeakMap<Node, Base> = new WeakMap()
|
||||
const store: WeakMap<Node, Dom> = new WeakMap()
|
||||
|
||||
export function ref(node: Node, instance?: Base | null) {
|
||||
export function cache(node: Node, instance?: Dom | null) {
|
||||
if (instance == null) {
|
||||
cache.delete(node)
|
||||
store.delete(node)
|
||||
} else {
|
||||
cache.set(node, instance)
|
||||
store.set(node, instance)
|
||||
}
|
||||
}
|
||||
|
||||
export function adopt<TVector extends Base>(node: Node): TVector
|
||||
export function adopt<TVector extends Base>(
|
||||
node?: Node | null,
|
||||
): TVector | null
|
||||
export function adopt<TVector extends Base>(
|
||||
node?: Node | null,
|
||||
): TVector | null {
|
||||
export function adopt(node: null): null
|
||||
export function adopt<T extends Node>(node: T): ElementMapping<T>
|
||||
export function adopt<T extends Dom>(node: Node | ChildNode): T
|
||||
export function adopt<T extends Dom>(node: Node | ChildNode | null): T | null
|
||||
export function adopt<T extends Node>(
|
||||
node?: T | null,
|
||||
): ElementMapping<T> | null
|
||||
export function adopt<T extends Node>(
|
||||
node?: T | null,
|
||||
): ElementMapping<T> | null {
|
||||
if (node == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
// make sure a node isn't already adopted
|
||||
const instance = cache.get(node)
|
||||
if (instance != null && instance instanceof Base) {
|
||||
return instance as TVector
|
||||
const instance = store.get(node)
|
||||
if (instance != null) {
|
||||
return instance as ElementMapping<T>
|
||||
}
|
||||
|
||||
const cls = Registry.getClass(node)
|
||||
return new cls(node) // eslint-disable-line new-cap
|
||||
const Type = Registry.getClass(node)
|
||||
return new Type(node) as ElementMapping<T>
|
||||
}
|
||||
|
||||
let adopter = adopt
|
||||
|
||||
export type Target<TVector extends Base = Base> = TVector | Node | string
|
||||
export type Target<T extends Dom = Dom> = T | Node | string
|
||||
|
||||
export function makeInstance<TVector extends Base>(
|
||||
node?: TVector | Node | string,
|
||||
export function makeInstance(): Svg
|
||||
export function makeInstance(node: undefined | null): Svg
|
||||
export function makeInstance<T extends Dom>(instance: T): T
|
||||
export function makeInstance<T extends Dom>(target: Target<T>): T
|
||||
export function makeInstance<T extends Node>(node: T): ElementMapping<T>
|
||||
export function makeInstance<T extends keyof SVGTagMapping>(
|
||||
tagName: T,
|
||||
): SVGTagMapping[T]
|
||||
export function makeInstance<T extends keyof SVGTagMapping>(
|
||||
tagName: T,
|
||||
isHTML: false,
|
||||
): SVGTagMapping[T]
|
||||
export function makeInstance<T extends keyof HTMLTagMapping>(
|
||||
tagName: T,
|
||||
isHTML: true,
|
||||
): HTMLTagMapping[T]
|
||||
export function makeInstance<T extends Dom>(
|
||||
tagName: string,
|
||||
isHTML: boolean,
|
||||
): T
|
||||
export function makeInstance<T extends Dom>(
|
||||
node?: T | Node | string,
|
||||
isHTML = false,
|
||||
): TVector {
|
||||
): T {
|
||||
if (node == null) {
|
||||
const root = Registry.getRoot()
|
||||
return new root() // eslint-disable-line new-cap
|
||||
const Root = Registry.getRoot()
|
||||
return new Root() as T
|
||||
}
|
||||
|
||||
if (node instanceof Base) {
|
||||
return node
|
||||
if (node instanceof Node) {
|
||||
return adopter(node) as T
|
||||
}
|
||||
|
||||
if (typeof node === 'object') {
|
||||
return adopter(node)
|
||||
return node
|
||||
}
|
||||
|
||||
if (typeof node === 'string' && node.charAt(0) !== '<') {
|
||||
return adopter(Global.document.querySelector(node)) as TVector
|
||||
return adopter(Global.document.querySelector(node)) as T
|
||||
}
|
||||
|
||||
// Make sure, that HTML elements are created with the correct namespace
|
||||
@ -73,7 +98,7 @@ export namespace Adopter {
|
||||
// make sure, that element doesnt have its wrapper attached
|
||||
wrapper.firstChild!.remove()
|
||||
|
||||
return result as TVector
|
||||
return result as T
|
||||
}
|
||||
|
||||
export function mock(instance = adopt) {
|
||||
|
@ -4,16 +4,14 @@ import { Registry } from './registry'
|
||||
export abstract class Base {}
|
||||
|
||||
export namespace Base {
|
||||
type Class = { new (...arguments_: any[]): Base }
|
||||
|
||||
export function register(name: string, asRoot?: boolean) {
|
||||
return <TClass extends Class>(ctor: TClass) => {
|
||||
return (ctor: Registry.Definition) => {
|
||||
Registry.register(ctor, name, asRoot)
|
||||
}
|
||||
}
|
||||
|
||||
export function mixin(...source: any[]) {
|
||||
return <TClass extends Class>(ctor: TClass) => {
|
||||
return (ctor: Registry.Definition) => {
|
||||
Obj.applyMixins(ctor, ...source)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export namespace Decorator {
|
||||
const raw = descriptor.value
|
||||
descriptor.value = function (
|
||||
this: VectorElement<TSVGElement>,
|
||||
...arguments_: any[]
|
||||
...args: any[]
|
||||
) {
|
||||
const defs = this.defs()
|
||||
if (defs == null) {
|
||||
@ -18,7 +18,7 @@ export namespace Decorator {
|
||||
'Please ensure that the current element is attached into any SVG context.',
|
||||
)
|
||||
}
|
||||
return raw.call(this, ...arguments_)
|
||||
return raw.call(this, ...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Attrs, Class } from '../../types'
|
||||
import type { Attrs } from '../../types'
|
||||
import { Global } from '../../global'
|
||||
import { DomUtil } from '../../util/dom'
|
||||
import { Attr } from '../../util/attr'
|
||||
import { Svg } from '../container/svg'
|
||||
import { DomUtil } from '../../util/dom'
|
||||
import { Adopter } from '../adopter'
|
||||
import { Data } from './data'
|
||||
import { Affix } from './affix'
|
||||
import { Style } from './style'
|
||||
import { Primer } from './primer'
|
||||
import { Memory } from './memory'
|
||||
@ -12,30 +12,70 @@ import { Listener } from './listener'
|
||||
import { ClassName } from './classname'
|
||||
import { Transform } from './transform'
|
||||
import { EventEmitter } from './event-emitter'
|
||||
import { ElementMapping, HTMLTagMapping, SVGTagMapping } from '../types'
|
||||
import { Registry } from '../registry'
|
||||
|
||||
@Dom.register('Dom')
|
||||
@Dom.mixin(EventEmitter, ClassName, Style, Data, Memory, Listener, Transform)
|
||||
@Dom.mixin(
|
||||
ClassName,
|
||||
Style,
|
||||
Transform,
|
||||
Data,
|
||||
Affix,
|
||||
Memory,
|
||||
EventEmitter,
|
||||
Listener,
|
||||
)
|
||||
export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
constructor()
|
||||
constructor(node: Node)
|
||||
constructor(attrs: Attrs | null)
|
||||
constructor(node?: TNode | string | null, attrs?: Attrs | null)
|
||||
constructor(node?: TNode | string | Attrs | null, attrs?: Attrs | null)
|
||||
constructor(node?: TNode | string | Attrs | null, attrs?: Attrs | null) {
|
||||
super(node, attrs)
|
||||
this.restoreAffix()
|
||||
Adopter.cache(this.node, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first child of the element.
|
||||
*/
|
||||
first<T extends Dom = Dom>(): T | null {
|
||||
return Dom.adopt<T>(this.node.firstChild)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last child of the element.
|
||||
*/
|
||||
last<T extends Dom = Dom>(): T | null {
|
||||
return Dom.adopt<T>(this.node.lastChild)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an element on a given position in the element's children array.
|
||||
*/
|
||||
get<T extends Dom = Dom>(index: number): T | null {
|
||||
return Dom.adopt<T>(this.node.childNodes[index])
|
||||
}
|
||||
|
||||
find<T extends Dom = Dom>(selectors: string): T[] {
|
||||
return Dom.find<T>(selectors, DomUtil.toElement(this.node))
|
||||
/**
|
||||
* Returns an array of elements matching the given selector.
|
||||
*/
|
||||
find<T extends Dom = Dom>(selector: string): T[] {
|
||||
return Dom.find<T>(selector, DomUtil.toElement(this.node))
|
||||
}
|
||||
|
||||
findOne<T extends Dom = Dom>(selectors: string): T | null {
|
||||
return Dom.findOne<T>(selectors, DomUtil.toElement(this.node))
|
||||
/**
|
||||
* Returns the first element matching the given selector.
|
||||
*/
|
||||
findOne<T extends Dom = Dom>(selector: string): T | null {
|
||||
return Dom.findOne<T>(selector, DomUtil.toElement(this.node))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the element matching the given selector.
|
||||
*/
|
||||
matches(selector: string): boolean {
|
||||
const elem = DomUtil.toElement(this.node)
|
||||
const node = this.node as any
|
||||
@ -51,6 +91,9 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return matcher ? matcher.call(elem, selector) : false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of children elements.
|
||||
*/
|
||||
children<T extends Dom>(): T[] {
|
||||
const elems: T[] = []
|
||||
this.node.childNodes.forEach((node) => {
|
||||
@ -59,6 +102,9 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return elems
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the element.
|
||||
*/
|
||||
clear() {
|
||||
while (this.node.lastChild) {
|
||||
this.node.lastChild.remove()
|
||||
@ -66,15 +112,22 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
clone<T extends Dom>(deep = true): T {
|
||||
/**
|
||||
* Returns an exact copy of the element.
|
||||
*/
|
||||
clone(deep = true) {
|
||||
// write dom data to the dom so the clone can pickup the data
|
||||
this.storeAssets()
|
||||
this.storeAffix(deep)
|
||||
// clone element and assign new id
|
||||
const ctor = this.constructor as new (node: Node) => T
|
||||
// eslint-disable-next-line new-cap
|
||||
return new ctor(DomUtil.assignNewId(this.node.cloneNode(deep)))
|
||||
const Ctor = this.constructor as new (node: Node) => ElementMapping<TNode>
|
||||
return new Ctor(DomUtil.assignNewId(this.node.cloneNode(deep)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all the children of the element.
|
||||
* Deep traversing is also possible by passing `true` as the second argument.
|
||||
* @returns
|
||||
*/
|
||||
eachChild<T extends Dom>(
|
||||
iterator: (this: T, child: T, index: number, children: T[]) => void,
|
||||
deep?: boolean,
|
||||
@ -91,21 +144,58 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
indexOf(element: Dom): number {
|
||||
/**
|
||||
* Returns the index of given node.
|
||||
* Returns `-1` when it is not a child.
|
||||
*/
|
||||
indexOf(node: Node): number
|
||||
/**
|
||||
* Returns the index of given element.
|
||||
* Returns `-1` when it is not a child.
|
||||
*/
|
||||
indexOf(element: Dom): number
|
||||
/**
|
||||
* Returns the index of given element or node.
|
||||
* Returns `-1` when it is not a child.
|
||||
*/
|
||||
indexOf(element: Dom | Node): number
|
||||
indexOf(element: Dom | Node): number {
|
||||
const children = Array.prototype.slice.call(this.node.childNodes) as Node[]
|
||||
return children.indexOf(element.node)
|
||||
return children.indexOf(element instanceof Node ? element : element.node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` when the given node is a child of the element.
|
||||
*/
|
||||
has(node: Node): boolean
|
||||
/**
|
||||
* Returns `true` when the given element is a child of the element.
|
||||
*/
|
||||
has(element: Dom): boolean
|
||||
/**
|
||||
* Returns `true` when the given element or node is a child of the element.
|
||||
*/
|
||||
has(element: Dom | Node): boolean
|
||||
has(element: Dom | Node): boolean {
|
||||
return this.indexOf(element) !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the element in it's parent.
|
||||
* Returns `-1` when the element do not have a parent.
|
||||
*/
|
||||
index(): number {
|
||||
const parent: Dom | null = this.parent()
|
||||
return parent ? parent.indexOf(this) : -1
|
||||
}
|
||||
|
||||
has(element: Dom): boolean {
|
||||
return this.indexOf(element) !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's id, generate new id if no id set.
|
||||
*/
|
||||
id(): string
|
||||
/**
|
||||
* Set id of the element.
|
||||
*/
|
||||
id(id: string | null): this
|
||||
id(id?: string | null) {
|
||||
const elem = DomUtil.toElement(this.node)
|
||||
@ -119,23 +209,38 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return typeof id === 'undefined' ? this.attr('id') : this.attr('id', id)
|
||||
}
|
||||
|
||||
parent<T extends Dom = Dom>(selectors?: string | Class): T | null {
|
||||
/**
|
||||
* Returns the parent element if exist.
|
||||
*/
|
||||
parent<T extends Dom = Dom>(): T | null
|
||||
/**
|
||||
* Iterates over the ancestors and returns the ancestor mathcing the selector.
|
||||
*/
|
||||
parent<T extends Dom = Dom>(selector: string): T | null
|
||||
/**
|
||||
* Iterates over the ancestors and returns the ancestor mathcing the type.
|
||||
*/
|
||||
parent<T extends Dom = Dom>(parentType: Registry.Definition): T | null
|
||||
parent<T extends Dom = Dom>(selector?: string | Registry.Definition): T | null
|
||||
parent<T extends Dom = Dom>(
|
||||
selector?: string | Registry.Definition,
|
||||
): T | null {
|
||||
if (this.node.parentNode == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
let parent: T | null = Dom.adopt<T>(this.node.parentNode)
|
||||
|
||||
if (selectors == null) {
|
||||
if (selector == null) {
|
||||
return parent
|
||||
}
|
||||
|
||||
// loop trough ancestors if type is given
|
||||
do {
|
||||
if (
|
||||
typeof selectors === 'string'
|
||||
? parent.matches(selectors)
|
||||
: parent instanceof selectors
|
||||
typeof selector === 'string'
|
||||
? parent.matches(selector)
|
||||
: parent instanceof selector
|
||||
) {
|
||||
return parent
|
||||
}
|
||||
@ -144,12 +249,36 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return null
|
||||
}
|
||||
|
||||
parents<T extends Dom = Dom>(): T[]
|
||||
parents<T extends Dom = Dom>(until: null): T[]
|
||||
parents<T extends Dom = Dom>(untilElement: Dom): T[]
|
||||
parents<T extends Dom = Dom>(untilNode: Node): T[]
|
||||
parents<T extends Dom = Dom>(untilSelector: string): T[]
|
||||
parents<T extends Dom = Dom>(until: Adopter.Target<Dom> | null): T[]
|
||||
parents<T extends Dom = Dom>(until?: Adopter.Target<Dom> | null) {
|
||||
const stop = until ? Adopter.makeInstance<Dom>(until) : null
|
||||
const parents: T[] = []
|
||||
let parent = this.parent()
|
||||
while (parent && !parent.isDocument() && !parent.isDocumentFragment()) {
|
||||
parents.push(parent as T)
|
||||
if (stop && parent.node === stop.node) {
|
||||
break
|
||||
}
|
||||
parent = parent.parent()
|
||||
}
|
||||
return parents
|
||||
}
|
||||
|
||||
add<T extends Dom>(element: T, index?: number): this
|
||||
add<T extends Node>(node: T, index?: number): this
|
||||
add(selector: string, index?: number): this
|
||||
add<T extends Dom>(element: Adopter.Target<T>, index?: number): this
|
||||
add<T extends Dom>(element: Adopter.Target<T>, index?: number): this {
|
||||
const instance = Adopter.makeInstance<T>(element)
|
||||
|
||||
// If non-root svg nodes are added we have to remove their namespaces
|
||||
if (instance.isSVGSVGElement()) {
|
||||
const svg = Dom.adopt<Svg>(instance.node)
|
||||
const svg = Dom.adopt(instance.node as SVGSVGElement)
|
||||
svg.removeNamespace()
|
||||
}
|
||||
|
||||
@ -162,40 +291,72 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
append<T extends Dom>(element: T): this
|
||||
append<T extends Node>(node: T): this
|
||||
append(selector: string): this
|
||||
append<T extends Dom>(element: Adopter.Target<T>): this
|
||||
append<T extends Dom>(element: Adopter.Target<T>): this {
|
||||
return this.add(element)
|
||||
}
|
||||
|
||||
prepend<T extends Dom>(element: T): this
|
||||
prepend<T extends Node>(node: T): this
|
||||
prepend(selector: string): this
|
||||
prepend<T extends Dom>(element: Adopter.Target<T>): this
|
||||
prepend<T extends Dom>(element: Adopter.Target<T>): this {
|
||||
return this.add(element, 0)
|
||||
}
|
||||
|
||||
appendTo<T extends Dom>(parent: Adopter.Target<T>): this {
|
||||
return this.addTo(parent)
|
||||
}
|
||||
|
||||
addTo<T extends Dom>(parentElement: T, index?: number): this
|
||||
addTo<T extends Node>(parentNode: T, index?: number): this
|
||||
addTo(selector: string, index?: number): this
|
||||
addTo<T extends Dom>(parent: Adopter.Target<T>, index?: number): this
|
||||
addTo<T extends Dom>(parent: Adopter.Target<T>, index?: number): this {
|
||||
return Adopter.makeInstance<T>(parent).put(this, index)
|
||||
}
|
||||
|
||||
appendTo<T extends Dom>(parentElement: T): this
|
||||
appendTo<T extends Node>(parentNode: T): this
|
||||
appendTo(selector: string): this
|
||||
appendTo<T extends Dom>(parent: Adopter.Target<T>): this
|
||||
appendTo<T extends Dom>(parent: Adopter.Target<T>): this {
|
||||
return this.addTo(parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given element to the end fo child list or the optional child
|
||||
* position and returns the added element.
|
||||
*/
|
||||
put<T extends Dom>(element: T, index?: number): T
|
||||
/**
|
||||
* Adds the given node to the end fo child list or the optional child position
|
||||
* and returns the added element.
|
||||
*/
|
||||
put<T extends Node>(node: T, index?: number): ElementMapping<T>
|
||||
/**
|
||||
* Adds the node matching the selector to end fo child list or the optional
|
||||
* child position and returns the added element.
|
||||
*/
|
||||
put<T extends Dom>(selector: string, index?: number): T
|
||||
put<T extends Dom>(element: Adopter.Target<T>, index?: number): T
|
||||
put<T extends Dom>(element: Adopter.Target<T>, index?: number): T {
|
||||
const instance = Adopter.makeInstance<T>(element)
|
||||
this.add(instance, index)
|
||||
return instance
|
||||
}
|
||||
|
||||
putIn<T extends Dom>(parentElement: T, index?: number): T
|
||||
putIn<T extends Node>(parentNode: T, index?: number): ElementMapping<T>
|
||||
putIn<T extends Dom>(selector: string, index?: number): T
|
||||
putIn<T extends Dom>(parent: Adopter.Target<T>, index?: number): T
|
||||
putIn<T extends Dom>(parent: Adopter.Target<T>, index?: number): T {
|
||||
return Adopter.makeInstance<T>(parent).add(this, index)
|
||||
}
|
||||
|
||||
element<T extends Dom>(nodeName: string, attrs?: Attrs | null): T {
|
||||
const elem = Adopter.makeInstance<T>(nodeName)
|
||||
if (attrs) {
|
||||
elem.attr(attrs)
|
||||
}
|
||||
return this.put(elem)
|
||||
}
|
||||
|
||||
replace<T extends Dom>(element: T, index?: number): T
|
||||
replace<T extends Node>(node: T, index?: number): ElementMapping<T>
|
||||
replace<T extends Dom>(selector: string, index?: number): T
|
||||
replace<T extends Dom>(element: Adopter.Target<T>, index?: number): T
|
||||
replace<T extends Dom>(element: Adopter.Target<T>): T {
|
||||
const instance = Adopter.makeInstance<T>(element)
|
||||
|
||||
@ -206,6 +367,22 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return instance
|
||||
}
|
||||
|
||||
element<T extends keyof SVGElementTagNameMap>(
|
||||
nodeName: T,
|
||||
attrs?: Attrs | null,
|
||||
): SVGTagMapping[T]
|
||||
element<T extends keyof HTMLElementTagNameMap>(
|
||||
nodeName: T,
|
||||
attrs?: Attrs | null,
|
||||
): HTMLTagMapping[T]
|
||||
element<T extends Dom>(nodeName: string, attrs?: Attrs | null): T {
|
||||
const elem = Adopter.makeInstance<T>(nodeName)
|
||||
if (attrs) {
|
||||
elem.attr(attrs)
|
||||
}
|
||||
return this.put(elem)
|
||||
}
|
||||
|
||||
remove() {
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
@ -215,11 +392,17 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
removeElement(element: Dom) {
|
||||
this.node.removeChild(element.node)
|
||||
removeElement(node: Node): this
|
||||
removeElement(element: Dom): this
|
||||
removeElement(element: Dom | Node) {
|
||||
this.node.removeChild(element instanceof Node ? element : element.node)
|
||||
return this
|
||||
}
|
||||
|
||||
before<T extends Dom>(element: T): this
|
||||
before<T extends Node>(node: T): this
|
||||
before(selector: string): this
|
||||
before<T extends Dom>(element: Adopter.Target<T>): this
|
||||
before<T extends Dom>(element: Adopter.Target<T>) {
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
@ -232,6 +415,10 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
after<T extends Dom>(element: T): this
|
||||
after<T extends Node>(node: T): this
|
||||
after(selector: string): this
|
||||
after<T extends Dom>(element: Adopter.Target<T>): this
|
||||
after<T extends Dom>(element: Adopter.Target<T>) {
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
@ -243,11 +430,19 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
insertBefore<T extends Dom>(element: T): this
|
||||
insertBefore<T extends Node>(node: T): this
|
||||
insertBefore(selector: string): this
|
||||
insertBefore<T extends Dom>(element: Adopter.Target<T>): this
|
||||
insertBefore<T extends Dom>(element: Adopter.Target<T>) {
|
||||
Adopter.makeInstance(element).before(this)
|
||||
return this
|
||||
}
|
||||
|
||||
insertAfter<T extends Dom>(element: T): this
|
||||
insertAfter<T extends Node>(node: T): this
|
||||
insertAfter(selector: string): this
|
||||
insertAfter<T extends Dom>(element: Adopter.Target<T>): this
|
||||
insertAfter<T extends Dom>(element: Adopter.Target<T>) {
|
||||
Adopter.makeInstance(element).after(this)
|
||||
return this
|
||||
@ -255,32 +450,34 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
|
||||
siblings<T extends Dom>(): T[]
|
||||
siblings<T extends Dom>(selfInclued?: boolean): T[]
|
||||
siblings<T extends Dom>(selectors: string, selfInclued?: boolean): T[]
|
||||
siblings(selectors?: string | boolean, selfInclued?: boolean) {
|
||||
siblings<T extends Dom>(selector: string, selfInclued?: boolean): T[]
|
||||
siblings(selector?: string | boolean, selfInclued?: boolean) {
|
||||
const parent = this.parent()
|
||||
const children = parent ? parent.children() : []
|
||||
|
||||
if (selectors == null) {
|
||||
if (selector == null) {
|
||||
return children.filter((child) => child !== this)
|
||||
}
|
||||
|
||||
if (typeof selectors === 'boolean') {
|
||||
return selectors ? children : children.filter((child) => child !== this)
|
||||
if (typeof selector === 'boolean') {
|
||||
return selector ? children : children.filter((child) => child !== this)
|
||||
}
|
||||
|
||||
return children.filter(
|
||||
(child) => child.matches(selectors) && (selfInclued || child !== this),
|
||||
(child) => child.matches(selector) && (selfInclued || child !== this),
|
||||
)
|
||||
}
|
||||
|
||||
next<T extends Dom>(selectors?: string): T | null {
|
||||
next<T extends Dom>(): T | null
|
||||
next<T extends Dom>(selector: string): T | null
|
||||
next<T extends Dom>(selector?: string): T | null {
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
const index = this.index()
|
||||
const children = parent.children<T>()
|
||||
for (let i = index + 1, l = children.length; i < l; i += 1) {
|
||||
const next = children[i]
|
||||
if (selectors == null || next.matches(selectors)) {
|
||||
if (selector == null || next.matches(selector)) {
|
||||
return next
|
||||
}
|
||||
}
|
||||
@ -288,7 +485,9 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return null
|
||||
}
|
||||
|
||||
nextAll<T extends Dom>(selectors?: string): T[] {
|
||||
nextAll<T extends Dom>(): T[]
|
||||
nextAll<T extends Dom>(selector: string): T[]
|
||||
nextAll<T extends Dom>(selector?: string): T[] {
|
||||
const result: T[] = []
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
@ -296,7 +495,7 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
const children = parent.children<T>()
|
||||
for (let i = index + 1, l = children.length; i < l; i += 1) {
|
||||
const next = children[i]
|
||||
if (selectors == null || next.matches(selectors)) {
|
||||
if (selector == null || next.matches(selector)) {
|
||||
result.push(next)
|
||||
}
|
||||
}
|
||||
@ -304,14 +503,16 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return result
|
||||
}
|
||||
|
||||
prev<T extends Dom>(selectors?: string): T | null {
|
||||
prev<T extends Dom>(): T | null
|
||||
prev<T extends Dom>(selector: string): T | null
|
||||
prev<T extends Dom>(selector?: string): T | null {
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
const index = this.index()
|
||||
const children = parent.children<T>()
|
||||
for (let i = index - 1; i >= 0; i -= 1) {
|
||||
const previous = children[i]
|
||||
if (selectors == null || previous.matches(selectors)) {
|
||||
if (selector == null || previous.matches(selector)) {
|
||||
return previous
|
||||
}
|
||||
}
|
||||
@ -320,7 +521,9 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return null
|
||||
}
|
||||
|
||||
prevAll<T extends Dom>(selectors?: string): T[] {
|
||||
prevAll<T extends Dom>(): T[]
|
||||
prevAll<T extends Dom>(selector: string): T[]
|
||||
prevAll<T extends Dom>(selector?: string): T[] {
|
||||
const result: T[] = []
|
||||
const parent = this.parent()
|
||||
if (parent) {
|
||||
@ -328,7 +531,7 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
const children = parent.children<T>()
|
||||
for (let i = index - 1; i >= 0; i -= 1) {
|
||||
const previous = children[i]
|
||||
if (selectors == null || previous.matches(selectors)) {
|
||||
if (selector == null || previous.matches(selector)) {
|
||||
result.push(previous)
|
||||
}
|
||||
}
|
||||
@ -370,6 +573,10 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
wrap<T extends Dom>(element: T): this
|
||||
wrap<T extends Node>(node: T): this
|
||||
wrap(selector: string): this
|
||||
wrap<T extends Dom>(element: Adopter.Target<T>): this
|
||||
wrap<T extends Dom>(node: Adopter.Target<T>): this {
|
||||
const parent = this.parent()
|
||||
if (!parent) {
|
||||
@ -385,11 +592,6 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
return this
|
||||
}
|
||||
|
||||
storeAssets() {
|
||||
this.eachChild(() => this.storeAssets())
|
||||
return this
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.id()
|
||||
}
|
||||
@ -441,7 +643,7 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
// The default for exports is, that the outerNode is included
|
||||
isOuterXML = isOuterXML == null ? true : isOuterXML
|
||||
|
||||
this.storeAssets()
|
||||
this.storeAffix(true)
|
||||
|
||||
let current: Dom = this // eslint-disable-line
|
||||
|
||||
@ -507,19 +709,16 @@ export class Dom<TNode extends Node = Node> extends Primer<TNode> {
|
||||
|
||||
export interface Dom<TNode extends Node = Node>
|
||||
extends ClassName<TNode>,
|
||||
EventEmitter<TNode>,
|
||||
Style<TNode>,
|
||||
Transform<TNode>,
|
||||
Data<TNode>,
|
||||
Affix<TNode>,
|
||||
Memory<TNode>,
|
||||
Listener<TNode>,
|
||||
Transform<TNode> {}
|
||||
EventEmitter<TNode>,
|
||||
Listener<TNode> {}
|
||||
|
||||
export namespace Dom {
|
||||
export function adopt<T extends Dom>(node: Node): T
|
||||
export function adopt<T extends Dom>(node?: Node | null): T | null
|
||||
export function adopt<T extends Dom>(node?: Node | null) {
|
||||
return Adopter.adopt<T>(node)
|
||||
}
|
||||
export const adopt = Adopter.adopt
|
||||
|
||||
export function find<T extends Dom = Dom>(
|
||||
selectors: string,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Attr } from '../../util/attr'
|
||||
import { DomUtil } from '../../util/dom'
|
||||
import { Attrs, Class } from '../../types'
|
||||
import { Attrs } from '../../types'
|
||||
import { Color } from '../../struct/color'
|
||||
import { NumberArray } from '../../struct/number-array'
|
||||
import { Registry } from '../registry'
|
||||
@ -13,34 +13,29 @@ export abstract class Primer<TNode extends Node> extends Base {
|
||||
return this.node.nodeName
|
||||
}
|
||||
|
||||
constructor()
|
||||
constructor(node: Node)
|
||||
constructor(attrs: Attrs | null)
|
||||
constructor(node?: TNode | string | null, attrs?: Attrs | null)
|
||||
constructor(node?: TNode | string | Attrs | null, attrs?: Attrs | null)
|
||||
constructor(node?: TNode | string | Attrs | null, attrs?: Attrs | null) {
|
||||
super()
|
||||
|
||||
let attributes: Attrs | null | undefined
|
||||
if (DomUtil.isNode(node)) {
|
||||
this.node = node
|
||||
if (attrs) {
|
||||
this.attr(attrs)
|
||||
}
|
||||
attributes = attrs
|
||||
} else {
|
||||
const ctor = this.constructor as Class
|
||||
const ctor = this.constructor as Registry.Definition
|
||||
const name = typeof node === 'string' ? node : Registry.getTagName(ctor)
|
||||
if (name) {
|
||||
this.node = DomUtil.createNode<any>(name)
|
||||
const ats = node != null && typeof node !== 'string' ? node : attrs
|
||||
if (ats) {
|
||||
this.attr(ats)
|
||||
}
|
||||
attributes = node != null && typeof node !== 'string' ? node : attrs
|
||||
} else {
|
||||
throw new Error(
|
||||
`Can not initialize "${ctor.name}" with unknown node name`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
this.attr(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
attr(): Attrs
|
||||
|
@ -98,20 +98,28 @@ export class Transform<TNode extends Node>
|
||||
)
|
||||
}
|
||||
|
||||
rotate(angle: number): this
|
||||
rotate(angle: number, cx: number, cy: number): this
|
||||
rotate(angle: number, cx?: number, cy?: number) {
|
||||
return this.transform({ rotate: angle, ox: cx, oy: cy }, true)
|
||||
}
|
||||
|
||||
skew(x: number, y: number): this
|
||||
skew(x: number, y: number, cx: number, cy: number): this
|
||||
skew(x: number, y: number, cx?: number, cy?: number) {
|
||||
return arguments.length === 1 || arguments.length === 3
|
||||
? this.transform({ skew: x, ox: y, oy: cx }, true)
|
||||
: this.transform({ skew: [x, y], ox: cx, oy: cy }, true)
|
||||
}
|
||||
|
||||
shear(lam: number): this
|
||||
shear(lam: number, cx: number, cy: number): this
|
||||
shear(lam: number, cx?: number, cy?: number) {
|
||||
return this.transform({ shear: lam, ox: cx, oy: cy }, true)
|
||||
}
|
||||
|
||||
scale(x: number, y: number): this
|
||||
scale(x: number, y: number, cx: number, cy: number): this
|
||||
scale(x: number, y: number, cx?: number, cy?: number) {
|
||||
return arguments.length === 1 || arguments.length === 3
|
||||
? this.transform({ scale: x, ox: y, oy: cx }, true)
|
||||
|
@ -10,7 +10,7 @@ import { Matrix } from '../struct/matrix'
|
||||
import { UNumber } from '../struct/unumber'
|
||||
import { Vector } from './vector'
|
||||
|
||||
@VectorElement.register('Element')
|
||||
@VectorElement.register('Vector')
|
||||
export class VectorElement<
|
||||
TSVGElement extends SVGElement = SVGElement
|
||||
> extends Vector<TSVGElement> {
|
||||
@ -258,7 +258,7 @@ export class VectorElement<
|
||||
const retry = (node: SVGGraphicsElement) =>
|
||||
DomUtil.withSvgContext((svg) => {
|
||||
try {
|
||||
const cloned = this.clone<VectorElement>().addTo(svg).show()
|
||||
const cloned = this.clone().addTo(svg).show()
|
||||
const box = DomUtil.toElement<SVGGraphicsElement>(
|
||||
cloned.node,
|
||||
).getBBox()
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { Class } from '../types'
|
||||
import { Str } from '../util/str'
|
||||
import { Base } from './base'
|
||||
|
||||
export namespace Registry {
|
||||
let root: Class
|
||||
const cache: Record<string, Class> = {}
|
||||
export type Definition = { new (...args: any[]): Base }
|
||||
|
||||
export function register(ctor: Class, name = ctor.name, asRoot?: boolean) {
|
||||
let root: Definition
|
||||
const cache: Record<string, Definition> = {}
|
||||
|
||||
export function register(
|
||||
ctor: Definition,
|
||||
name = ctor.name,
|
||||
asRoot?: boolean,
|
||||
) {
|
||||
cache[name] = ctor
|
||||
|
||||
if (asRoot) {
|
||||
@ -15,28 +21,34 @@ export namespace Registry {
|
||||
return ctor
|
||||
}
|
||||
|
||||
export function getClass<TClass extends Class = Class>(node: Node): TClass
|
||||
export function getClass<TClass extends Class = Class>(name: string): TClass
|
||||
export function getClass<TClass extends Class = Class>(node: string | Node) {
|
||||
export function getClass<TDefinition extends Definition = Definition>(
|
||||
node: Node,
|
||||
): TDefinition
|
||||
export function getClass<TDefinition extends Definition = Definition>(
|
||||
name: string,
|
||||
): TDefinition
|
||||
export function getClass<TDefinition extends Definition = Definition>(
|
||||
node: string | Node,
|
||||
) {
|
||||
if (typeof node === 'string') {
|
||||
return cache[node] as TClass
|
||||
return cache[node] as TDefinition
|
||||
}
|
||||
|
||||
const nodeName = node.nodeName || 'Dom'
|
||||
const nodeName = node.nodeName
|
||||
let className = Str.ucfirst(nodeName)
|
||||
|
||||
if (nodeName === '#document-fragment') {
|
||||
className = 'Fragment'
|
||||
} else if (
|
||||
className === 'LinearGradient' ||
|
||||
className === 'RadialGradient'
|
||||
if (
|
||||
node instanceof SVGElement &&
|
||||
(className === 'LinearGradient' || className === 'RadialGradient')
|
||||
) {
|
||||
className = 'Gradient'
|
||||
} else if (nodeName === '#document-fragment') {
|
||||
className = 'Fragment'
|
||||
} else if (!Registry.hasClass(className)) {
|
||||
className = 'Dom'
|
||||
className = node instanceof SVGElement ? 'Vector' : 'Dom'
|
||||
}
|
||||
|
||||
return cache[className] as TClass
|
||||
return cache[className] as TDefinition
|
||||
}
|
||||
|
||||
export function hasClass(name: string) {
|
||||
@ -47,7 +59,7 @@ export namespace Registry {
|
||||
return root
|
||||
}
|
||||
|
||||
export function getTagName(cls: Class) {
|
||||
export function getTagName(cls: Definition) {
|
||||
const keys = Object.keys(cache)
|
||||
for (let i = 0, l = keys.length; i < l; i += 1) {
|
||||
const key = keys[i]
|
||||
|
68
packages/x6-vector/src/element/types.ts
Normal file
68
packages/x6-vector/src/element/types.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { A } from './container/a'
|
||||
import { ClipPath } from './container/clippath'
|
||||
import { Defs } from './container/defs'
|
||||
import { G } from './container/g'
|
||||
import { Gradient } from './container/gradient'
|
||||
import { Marker } from './container/marker'
|
||||
import { Mask } from './container/mask'
|
||||
import { Pattern } from './container/pattern'
|
||||
import { Svg } from './container/svg'
|
||||
import { Symbol } from './container/symbol'
|
||||
import { Dom } from './dom'
|
||||
import { VectorElement } from './element'
|
||||
import { Circle } from './shape/circle'
|
||||
import { Ellipse } from './shape/ellipse'
|
||||
import { ForeignObject } from './shape/foreignobject'
|
||||
import { Fragment } from './shape/fragment'
|
||||
import { Image } from './shape/image'
|
||||
import { Line } from './shape/line'
|
||||
import { Path } from './shape/path'
|
||||
import { Polygon } from './shape/polygon'
|
||||
import { Polyline } from './shape/polyline'
|
||||
import { Rect } from './shape/rect'
|
||||
import { Style } from './shape/style'
|
||||
import { Text } from './shape/text'
|
||||
import { TSpan } from './shape/tspan'
|
||||
import { Use } from './shape/use'
|
||||
|
||||
// prettier-ignore
|
||||
export type ElementMapping<T> =
|
||||
T extends SVGAElement ? A :
|
||||
T extends SVGClipPathElement ? ClipPath :
|
||||
T extends SVGDefsElement ? Defs :
|
||||
T extends SVGGElement ? G :
|
||||
T extends SVGLinearGradientElement ? Gradient :
|
||||
T extends SVGRadialGradientElement ? Gradient :
|
||||
T extends SVGMarkerElement ? Marker :
|
||||
T extends SVGMaskElement ? Mask :
|
||||
T extends SVGPatternElement ? Pattern :
|
||||
T extends SVGSVGElement ? Svg :
|
||||
T extends SVGSymbolElement ? Symbol : // eslint-disable-line
|
||||
|
||||
T extends SVGCircleElement ? Circle :
|
||||
T extends SVGEllipseElement ? Ellipse :
|
||||
T extends SVGForeignObjectElement ? ForeignObject :
|
||||
T extends DocumentFragment ? Fragment :
|
||||
T extends SVGImageElement ? Image :
|
||||
T extends SVGLineElement ? Line :
|
||||
T extends SVGPathElement ? Path :
|
||||
T extends SVGPolygonElement ? Polygon :
|
||||
T extends SVGPolylineElement ? Polyline :
|
||||
T extends SVGRectElement ? Rect :
|
||||
T extends SVGStyleElement ? Style :
|
||||
T extends SVGTextElement ? Text :
|
||||
T extends SVGTSpanElement ? TSpan :
|
||||
T extends SVGUseElement ? Use :
|
||||
|
||||
T extends SVGElement ? VectorElement<T>:
|
||||
T extends HTMLElement ? Dom<T> : Dom<Node>
|
||||
|
||||
export type HTMLElementMapping<T extends HTMLElement> = Dom<T>
|
||||
|
||||
export type SVGTagMapping = {
|
||||
[K in keyof SVGElementTagNameMap]: ElementMapping<SVGElementTagNameMap[K]>
|
||||
}
|
||||
|
||||
export type HTMLTagMapping = {
|
||||
[K in keyof HTMLElementTagNameMap]: Dom<HTMLElementTagNameMap[K]>
|
||||
}
|
Reference in New Issue
Block a user