@domql/element 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cache/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ export const cache = {}
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ export default {}
@@ -0,0 +1,165 @@
1
+ 'use strict'
2
+
3
+ import { isDefined, isFunction, isObjectLike, isProduction } from '@domql/utils'
4
+ import { TREE } from '../tree'
5
+ import { parseFilters, registry } from '../mixins'
6
+
7
+ // TODO: update these files
8
+ export const spotByPath = function (path) {
9
+ const element = this
10
+ const arr = [].concat(path)
11
+ let active = TREE[arr[0]]
12
+
13
+ if (!arr || !arr.length) return console.log(arr, 'on', element.key, 'is undefined')
14
+
15
+ while (active.key === arr[0]) {
16
+ arr.shift()
17
+ if (!arr.length) break
18
+ active = active[arr[0]]
19
+ if (!active) return
20
+ }
21
+
22
+ return active
23
+ }
24
+
25
+ // TODO: update these files
26
+ export const lookup = function (key) {
27
+ const element = this
28
+ let { parent } = element
29
+
30
+ while (parent.key !== key) {
31
+ if (parent[key]) return parent[key]
32
+ parent = parent.parent
33
+ if (!parent) return
34
+ }
35
+
36
+ return parent
37
+ }
38
+
39
+ export const remove = function (params) {
40
+ const element = this
41
+ if (isFunction(element.node.remove)) element.node.remove()
42
+ else if (!isProduction()) {
43
+ console.warn('This item cant be removed')
44
+ element.log()
45
+ }
46
+ delete element.parent[element.key]
47
+ }
48
+
49
+ export const get = function (param) {
50
+ const element = this
51
+ return element[param]
52
+ }
53
+
54
+ export const setProps = function (param, options) {
55
+ const element = this
56
+ if (!param || !element.props) return
57
+ element.update({ props: param }, options)
58
+ return element
59
+ }
60
+
61
+ // export const set = function () {
62
+ // }
63
+
64
+ // export const update = function () {
65
+ // }
66
+
67
+ export const defineSetter = (element, key, get, set) =>
68
+ Object.defineProperty(element, key, { get, set })
69
+
70
+ export const keys = function () {
71
+ const element = this
72
+ const keys = []
73
+ for (const param in element) {
74
+ if (registry[param] && !parseFilters.elementKeys.includes(param)) { continue }
75
+ keys.push(param)
76
+ }
77
+ return keys
78
+ }
79
+
80
+ export const parse = function (excl = []) {
81
+ const element = this
82
+ const obj = {}
83
+ const keyList = keys.call(element)
84
+ keyList.forEach(v => {
85
+ if (excl.includes(v)) return
86
+ let val = element[v]
87
+ if (v === 'state') {
88
+ if (element.__ref && element.__ref.__hasRootState) return
89
+ if (isFunction(val && val.parse)) val = val.parse()
90
+ } else if (v === 'props') {
91
+ const { __element, update, ...props } = element[v]
92
+ obj[v] = props
93
+ } else if (isDefined(val)) obj[v] = val
94
+ })
95
+ return obj
96
+ }
97
+
98
+ export const parseDeep = function (excl = []) {
99
+ const element = this
100
+ const obj = parse.call(element, excl)
101
+ for (const v in obj) {
102
+ if (excl.includes(v)) return
103
+ if (isObjectLike(obj[v])) { obj[v] = parseDeep.call(obj[v], excl) }
104
+ }
105
+ return obj
106
+ }
107
+
108
+ export const log = function (...args) {
109
+ const element = this
110
+ const { __ref } = element
111
+ console.group(element.key)
112
+ if (args.length) {
113
+ args.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
114
+ } else {
115
+ console.log(__ref.path)
116
+ const keys = element.keys()
117
+ keys.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
118
+ }
119
+ console.groupEnd(element.key)
120
+ return element
121
+ }
122
+
123
+ export const nextElement = function () {
124
+ const element = this
125
+ const { key, parent } = element
126
+ const { __children } = parent.__ref
127
+
128
+ const currentIndex = __children.indexOf(key)
129
+ const nextChild = __children[currentIndex + 1]
130
+
131
+ return parent[nextChild]
132
+ }
133
+
134
+ export const previousElement = function (el) {
135
+ const element = el || this
136
+ const { key, parent } = element
137
+ const { __children } = parent.__ref
138
+
139
+ if (!__children) return
140
+
141
+ const currentIndex = __children.indexOf(key)
142
+ return parent[__children[currentIndex - 1]]
143
+ }
144
+
145
+ export const METHODS = [
146
+ 'set',
147
+ 'update',
148
+ 'remove',
149
+ 'updateContent',
150
+ 'removeContent',
151
+ 'lookup',
152
+ 'spotByPath',
153
+ 'keys',
154
+ 'parse',
155
+ 'setProps',
156
+ 'parseDeep',
157
+ 'if',
158
+ 'log',
159
+ 'nextElement',
160
+ 'previousElement'
161
+ ]
162
+
163
+ export const isMethod = function (param) {
164
+ return METHODS.includes(param)
165
+ }
package/methods/set.js ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ import { isDevelopment } from '@domql/utils'
4
+
5
+ import set from '../set'
6
+ import update from '../update'
7
+
8
+ import {
9
+ lookup,
10
+ setProps,
11
+ remove,
12
+ spotByPath,
13
+ log,
14
+ keys,
15
+ parse,
16
+ parseDeep,
17
+ nextElement,
18
+ previousElement
19
+ } from './'
20
+
21
+ import { removeContent, updateContent } from '../mixins/content'
22
+
23
+ export const addMethods = (element, parent) => {
24
+ const proto = {
25
+ set: set.bind(element),
26
+ update: update.bind(element),
27
+ remove: remove.bind(element),
28
+ updateContent: updateContent.bind(element),
29
+ removeContent: removeContent.bind(element),
30
+ setProps: setProps.bind(element),
31
+ lookup: lookup.bind(element),
32
+ spotByPath: spotByPath.bind(element),
33
+ parse: parse.bind(element),
34
+ parseDeep: parseDeep.bind(element),
35
+ keys: keys.bind(element),
36
+ nextElement: nextElement.bind(element),
37
+ previousElement: previousElement.bind(element)
38
+ }
39
+ if (isDevelopment()) proto.log = log.bind(element)
40
+ Object.setPrototypeOf(element, proto)
41
+ }
package/methods/v2.js ADDED
@@ -0,0 +1,82 @@
1
+ 'use strict'
2
+
3
+ import { isDefined, isFunction, isObjectLike } from '@domql/utils'
4
+ import { parseFilters, registry } from '../mixins'
5
+
6
+ export const defineSetter = (element, key, get, set) =>
7
+ Object.defineProperty(element, key, { get, set })
8
+
9
+ export const keys = function () {
10
+ const element = this
11
+ const keys = []
12
+ for (const param in element) {
13
+ if (registry[param] && !parseFilters.elementKeys.includes(param)) { continue }
14
+ keys.push(param)
15
+ }
16
+ return keys
17
+ }
18
+
19
+ export const parse = function (excl = []) {
20
+ const element = this
21
+ const obj = {}
22
+ const keyList = keys.call(element)
23
+ keyList.forEach(v => {
24
+ if (excl.includes(v)) return
25
+ let val = element[v]
26
+ if (v === 'state') {
27
+ if (element.__ref && element.__ref.__hasRootState) return
28
+ if (isFunction(val && val.parse)) val = val.parse()
29
+ } else if (v === 'props') {
30
+ const { __element, update, ...props } = element[v]
31
+ obj[v] = props
32
+ } else if (isDefined(val)) obj[v] = val
33
+ })
34
+ return obj
35
+ }
36
+
37
+ export const parseDeep = function (excl = []) {
38
+ const element = this
39
+ const obj = parse.call(element, excl)
40
+ for (const v in obj) {
41
+ if (excl.includes(v)) return
42
+ if (isObjectLike(obj[v])) { obj[v] = parseDeep.call(obj[v], excl) }
43
+ }
44
+ return obj
45
+ }
46
+
47
+ export const log = function (...args) {
48
+ const element = this
49
+ const { __ref } = element
50
+ console.group(element.key)
51
+ if (args.length) {
52
+ args.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
53
+ } else {
54
+ console.log(__ref.path)
55
+ const keys = element.keys()
56
+ keys.forEach(v => console.log(`%c${v}:\n`, 'font-weight: bold', element[v]))
57
+ }
58
+ console.groupEnd(element.key)
59
+ return element
60
+ }
61
+
62
+ export const nextElement = function () {
63
+ const element = this
64
+ const { key, parent } = element
65
+ const { __children } = parent.__ref
66
+
67
+ const currentIndex = __children.indexOf(key)
68
+ const nextChild = __children[currentIndex + 1]
69
+
70
+ return parent[nextChild]
71
+ }
72
+
73
+ export const previousElement = function (el) {
74
+ const element = el || this
75
+ const { key, parent } = element
76
+ const { __children } = parent.__ref
77
+
78
+ if (!__children) return
79
+
80
+ const currentIndex = __children.indexOf(key)
81
+ return parent[__children[currentIndex - 1]]
82
+ }
package/mixins/attr.js ADDED
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ import { exec, isNot } from '@domql/utils'
4
+ import { report } from '@domql/report'
5
+
6
+ /**
7
+ * Recursively add attributes to a DOM node
8
+ */
9
+ export default (params, element, node) => {
10
+ const { __ref } = element
11
+ const { __attr } = __ref
12
+ if (isNot('object')) report('HTMLInvalidAttr', params)
13
+ if (params) {
14
+ for (const attr in params) {
15
+ const val = exec(params[attr], element)
16
+ // if (__attr[attr] === val) return
17
+ if (val && node.setAttribute) node.setAttribute(attr, val)
18
+ else if (node.removeAttribute) node.removeAttribute(attr)
19
+ __attr[attr] = val
20
+ }
21
+ }
22
+ console.groupEnd(params, __attr)
23
+ }
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ import { exec, isObject, isString } from '@domql/utils'
4
+
5
+ export const assignClass = (element) => {
6
+ const { key } = element
7
+ if (element.class === true) element.class = key
8
+ else if (!element.class && typeof key === 'string' && key.charAt(0) === '_' && key.charAt(1) !== '_') {
9
+ element.class = key.slice(1)
10
+ }
11
+ }
12
+
13
+ // stringifies class object
14
+ export const classify = (obj, element) => {
15
+ let className = ''
16
+ for (const item in obj) {
17
+ const param = obj[item]
18
+ if (typeof param === 'boolean' && param) className += ` ${item}`
19
+ else if (typeof param === 'string') className += ` ${param}`
20
+ else if (typeof param === 'function') {
21
+ className += ` ${exec(param, element)}`
22
+ }
23
+ }
24
+ return className
25
+ }
26
+
27
+ export const classList = (params, element) => {
28
+ if (!params) return
29
+ const { key } = element
30
+ if (params === true) params = element.class = { key }
31
+ if (isString(params)) params = element.class = { default: params }
32
+ if (isObject(params)) params = classify(params, element)
33
+ // TODO: fails on string
34
+ const className = params.replace(/\s+/g, ' ').trim()
35
+ if (element.ref) element.ref.class = className // TODO: this check is NOT needed in new DOMQL
36
+ return className
37
+ }
38
+
39
+ // LEGACY (still needed in old domql)
40
+ export const applyClassListOnNode = (params, element, node) => {
41
+ const className = classList(params, element)
42
+ node.classList = className
43
+ return className
44
+ }
45
+
46
+ export default (params, element, node) => {
47
+ applyClassListOnNode(params, element, node)
48
+ }
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ import { isFunction } from '@domql/utils'
4
+ import set from '../set'
5
+
6
+ export const updateContent = function (params, options) {
7
+ const element = this
8
+
9
+ if (!element.content) return
10
+ if (element.content.update) element.content.update(params, options)
11
+ }
12
+
13
+ export const removeContent = function (el) {
14
+ const element = el || this
15
+ const { __ref } = element
16
+
17
+ if (element.content) {
18
+ if (element.content.node) {
19
+ if (element.content.tag === 'fragment') element.node.innerHTML = ''
20
+ else element.node.removeChild(element.content.node)
21
+ }
22
+
23
+ const { __cached } = __ref
24
+ if (__cached && __cached.content) {
25
+ if (__cached.content.tag === 'fragment') __cached.content.parent.node.innerHTML = ''
26
+ else if (__cached.content && isFunction(__cached.content.remove)) __cached.content.remove()
27
+ }
28
+
29
+ delete element.content
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Appends anything as content
35
+ * an original one as a child
36
+ */
37
+ export const setContent = (param, element, node, options) => {
38
+ if (param && element) {
39
+ if (element.content.update) {
40
+ element.content.update({}, options)
41
+ } else {
42
+ set.call(element, param, options)
43
+ }
44
+ }
45
+ }
46
+
47
+ export default setContent
package/mixins/data.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ import { exec, isObject } from '@domql/utils'
4
+ import { report } from '@domql/report'
5
+
6
+ /**
7
+ * Apply data parameters on the DOM nodes
8
+ * this should only work if `showOnNode: true` is passed
9
+ */
10
+ export default (params, element, node) => {
11
+ if (params && params.showOnNode) {
12
+ if (!isObject(params)) report('HTMLInvalidData', params)
13
+
14
+ // Apply data params on node
15
+ for (const dataset in params) {
16
+ if (dataset !== 'showOnNode') {
17
+ node.dataset[dataset] = exec(params[dataset], element)
18
+ }
19
+ }
20
+ }
21
+ }
package/mixins/html.js ADDED
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ import { exec } from '@domql/utils'
4
+
5
+ /**
6
+ * Appends raw HTML as content
7
+ * an original one as a child
8
+ */
9
+ export default (param, element, node) => {
10
+ const prop = exec(param, element)
11
+ const { __ref } = element
12
+ if (prop !== __ref.__html) {
13
+ // const parser = new window.DOMParser()
14
+ // param = parser.parseFromString(param, 'text/html')
15
+ if (node.nodeName === 'SVG') node.textContent = prop
16
+ else node.innerHTML = prop
17
+
18
+ __ref.__html = prop
19
+ }
20
+ }
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ import attr from './attr'
4
+ import classList from './classList'
5
+ import content from './content'
6
+ import data from './data'
7
+ import html from './html'
8
+ import style from './style'
9
+ import text from './text'
10
+ import state from './state'
11
+ import registry from './registry'
12
+ export {
13
+ attr,
14
+ classList,
15
+ content,
16
+ data,
17
+ style,
18
+ text,
19
+ html,
20
+ state,
21
+ registry
22
+ }
23
+ export * from './registry'
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ import {
4
+ attr, classList, content,
5
+ data, html, state, style,
6
+ text
7
+ } from '.'
8
+
9
+ export default {
10
+ attr,
11
+ style,
12
+ text,
13
+ html,
14
+ content,
15
+ data,
16
+ class: classList,
17
+ state,
18
+
19
+ extend: {},
20
+ childExtend: {},
21
+ childExtendRecursive: {},
22
+ props: {},
23
+ path: {},
24
+ if: {},
25
+ define: {},
26
+ transform: {},
27
+ __ref: {},
28
+ __hash: {},
29
+ __text: {},
30
+ nextElement: {},
31
+ previousElement: {},
32
+ key: {},
33
+ tag: {},
34
+ query: {},
35
+ parent: {},
36
+ node: {},
37
+ set: {},
38
+ update: {},
39
+ setProps: {},
40
+ remove: {},
41
+ updateContent: {},
42
+ removeContent: {},
43
+ lookup: {},
44
+ spotByPath: {},
45
+ keys: {},
46
+ log: {},
47
+ parse: {},
48
+ parseDeep: {},
49
+ on: {},
50
+ component: {},
51
+ context: {},
52
+ $setStateCollection: {},
53
+ $setCollection: {},
54
+ $setPropsCollection: {}
55
+ }
56
+
57
+ // List of keys for .parse() and .parseDeep() to include in the result.
58
+ // Keys not in the array are excluded.
59
+ export const parseFilters = {
60
+ elementKeys: [
61
+ 'tag', 'text', 'style', 'attr', 'class', 'state', 'props',
62
+ 'data', 'content', 'html', 'on', 'key'
63
+ ],
64
+ propsKeys: ['__element', 'update'],
65
+ stateKeys: []
66
+ }
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ import { IGNORE_STATE_PARAMS } from '@domql/state'
4
+ import { exec, isObject } from '@domql/utils'
5
+
6
+ export const state = (params, element, node) => {
7
+ const state = exec(params, element)
8
+
9
+ if (isObject(state)) {
10
+ for (const param in state) {
11
+ if (IGNORE_STATE_PARAMS.includes(param)) continue
12
+ if (!state.hasOwnProperty(param)) continue
13
+ element.state[param] = exec(state[param], element)
14
+ }
15
+ }
16
+
17
+ return element
18
+ }
19
+
20
+ export default state
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ import { isObject, map } from '@domql/utils'
4
+ import { report } from '@domql/report'
5
+
6
+ /**
7
+ * Recursively add styles to a DOM node
8
+ */
9
+ export default (params, element, node) => {
10
+ if (params) {
11
+ if (isObject(params)) map(node.style, params, element)
12
+ else report('HTMLInvalidStyles', params)
13
+ }
14
+ }
package/mixins/text.js ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ import { create } from '..'
4
+ import {
5
+ exec,
6
+ isString,
7
+ replaceLiteralsWithObjectFields
8
+ } from '@domql/utils'
9
+
10
+ /**
11
+ * Creates a text node and appends into
12
+ * an original one as a child
13
+ */
14
+ export const asd = (param, element, node) => {
15
+ const prop = exec(param, element)
16
+ if (element.tag === 'string') {
17
+ node.nodeValue = prop
18
+ } else if (param !== undefined || param !== null) {
19
+ if (element.__text && element.__text.text !== prop) return
20
+ element.__text.text = prop
21
+ if (element.__text.node) element.__text.node.nodeValue = prop
22
+ else create({ tag: 'string', text: prop }, element, '__text')
23
+ }
24
+ }
25
+
26
+ export default (param, element, node) => {
27
+ let prop = exec(param, element)
28
+ if (isString(prop) && prop.includes('{{')) {
29
+ prop = replaceLiteralsWithObjectFields(prop, element.state)
30
+ }
31
+ if (element.tag === 'string') {
32
+ if (element.text === prop) return
33
+ node.nodeValue = prop
34
+ } else if (param !== undefined || param !== null) {
35
+ if (element.__text) {
36
+ if (element.__text.text === prop) return
37
+ element.__text.text = prop
38
+ if (element.__text.node) element.__text.node.nodeValue = prop
39
+ } else create({ tag: 'string', text: prop }, element, '__text')
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domql/element",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "source": "index.js",
15
15
  "files": [
16
+ "*/**.js",
16
17
  "*.js",
17
18
  "dist"
18
19
  ],
@@ -30,7 +31,7 @@
30
31
  "@domql/state": "latest",
31
32
  "@domql/utils": "latest"
32
33
  },
33
- "gitHead": "d01a7237a0065a5a439a03062064076c5fdc91b7",
34
+ "gitHead": "aab9993c65cc91165a2a9bc00a8117a217dc7565",
34
35
  "devDependencies": {
35
36
  "@babel/core": "^7.12.0"
36
37
  }
@@ -0,0 +1,60 @@
1
+ 'use strict'
2
+
3
+ import { exec, isArray, isObject, deepClone, deepMerge } from '@domql/utils'
4
+ import { IGNORE_PROPS_PARAMS } from './ignore'
5
+
6
+ import { inheritParentProps } from './inherit'
7
+
8
+ const createPropsStack = (element, parent) => {
9
+ const { props, __ref } = element
10
+ const propsStack = __ref.__props = inheritParentProps(element, parent)
11
+
12
+ if (isObject(props)) propsStack.push(props)
13
+ else if (props === 'inherit' && parent.props) propsStack.push(parent.props)
14
+ else if (props) propsStack.push(props)
15
+
16
+ if (isArray(__ref.__extend)) {
17
+ __ref.__extend.forEach(extend => {
18
+ if (extend.props) propsStack.push(extend.props)
19
+ })
20
+ }
21
+
22
+ __ref.__props = propsStack
23
+
24
+ return propsStack
25
+ }
26
+
27
+ export const syncProps = (props, element) => {
28
+ element.props = {}
29
+ const mergedProps = { update, __element: element }
30
+ props.forEach(v => {
31
+ if (IGNORE_PROPS_PARAMS.includes(v)) return
32
+ const execProps = exec(v, element)
33
+ if (isObject(execProps) && execProps.__element) return
34
+ element.props = deepMerge(
35
+ mergedProps,
36
+ deepClone(execProps, IGNORE_PROPS_PARAMS),
37
+ IGNORE_PROPS_PARAMS
38
+ )
39
+ })
40
+ element.props = mergedProps
41
+ return element.props
42
+ }
43
+
44
+ export const createProps = function (element, parent, cached) {
45
+ const propsStack = cached || createPropsStack(element, parent)
46
+ const { __ref } = element
47
+
48
+ if (propsStack.length) {
49
+ __ref.__props = propsStack
50
+ syncProps(propsStack, element)
51
+ element.props.update = update
52
+ }
53
+
54
+ return element
55
+ }
56
+
57
+ function update (props, options) {
58
+ const element = this.__element
59
+ element.update({ props }, options)
60
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ export const IGNORE_PROPS_PARAMS = ['update', '__element']
package/props/index.js ADDED
@@ -0,0 +1,6 @@
1
+ 'use strict'
2
+
3
+ export * from './ignore'
4
+ export * from './create'
5
+ export * from './inherit'
6
+ export * from './update'
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ import { exec, is, isString } from '@domql/utils'
4
+
5
+ const objectizeStringProperty = propValue => {
6
+ if (is(propValue)('string', 'number')) {
7
+ return { inheritedString: propValue }
8
+ }
9
+ return propValue
10
+ }
11
+
12
+ export const inheritParentProps = (element, parent) => {
13
+ let propsStack = []
14
+ const parentProps = exec(parent, parent.state).props
15
+
16
+ const matchParent = parent.props && parentProps[element.key]
17
+ const matchParentIsString = isString(matchParent)
18
+ const matchParentChildProps = parentProps && parentProps.childProps
19
+
20
+ if (matchParent) {
21
+ if (matchParentIsString) {
22
+ const inheritedStringExists = propsStack.filter(v => v.inheritedString)[0]
23
+ if (inheritedStringExists) inheritedStringExists.inheritedString = matchParent
24
+ else {
25
+ propsStack = [].concat(objectizeStringProperty(matchParent), propsStack)
26
+ }
27
+ } else {
28
+ propsStack.push(objectizeStringProperty(matchParent))
29
+ }
30
+ }
31
+ if (matchParentChildProps) propsStack.push(matchParentChildProps)
32
+
33
+ return propsStack
34
+ }
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ import { syncProps } from './create'
4
+ import { inheritParentProps } from './inherit'
5
+
6
+ export const updateProps = (newProps, element, parent) => {
7
+ const { __ref } = element
8
+ let propsStack = __ref.__props
9
+
10
+ const parentProps = inheritParentProps(element, parent)
11
+ if (parentProps) propsStack = __ref.__props = [].concat(parentProps, propsStack)
12
+ if (newProps) propsStack = __ref.__props = [].concat(newProps, propsStack)
13
+
14
+ if (propsStack) syncProps(propsStack, element)
15
+
16
+ return element
17
+ }
@@ -0,0 +1,61 @@
1
+ 'use strict'
2
+
3
+ import { create } from '../../src/element'
4
+
5
+ const dom = create({})
6
+
7
+ test('should create EMPTY element', () => {
8
+ expect(dom).toHaveProperty('key')
9
+ expect(dom).toHaveProperty('state')
10
+ expect(dom).toHaveProperty('parent')
11
+ expect(dom).toHaveProperty('node')
12
+ expect(dom).toHaveProperty('set')
13
+ expect(dom).toHaveProperty('update')
14
+ expect(dom).toHaveProperty('__ref')
15
+ })
16
+
17
+ test('should create valid DOM node', () => {
18
+ expect(dom.node).toBeInstanceOf(window.HTMLDivElement)
19
+ })
20
+
21
+ test('must be able to create valid PATH', () => {
22
+ expect(dom.__ref.__path).toStrictEqual([dom.key])
23
+ })
24
+
25
+ test('if it HAS a NODE, don\'t recreate', () => {
26
+ const node = document.createElement('div')
27
+ const dom2 = create({ node })
28
+ expect(dom2.node.parentElement).toBe(document.body)
29
+ })
30
+
31
+ test('create with number', () => {
32
+ const numb = create(0)
33
+ expect(numb.text).toBe(0)
34
+ expect(numb.tag).toBe('string')
35
+ expect(numb.node.nodeType).toBe(3) // #text
36
+ })
37
+
38
+ test('create with string', () => {
39
+ const str = create('hello')
40
+ expect(str.text).toBe('hello')
41
+ expect(str.tag).toBe('string')
42
+ expect(str.node.nodeType).toBe(3) // #text
43
+ })
44
+
45
+ test('creating conditions', () => {
46
+ const element = create({
47
+ data: { visible: true },
48
+ if: element => element.data.visible
49
+ })
50
+ expect(element.tag).toBe('div')
51
+ })
52
+
53
+ test('creating nesting', () => {
54
+ const element = create({
55
+ header: {
56
+ h1: {}
57
+ }
58
+ })
59
+ expect(element.header.tag).toBe('header')
60
+ expect(element.header.h1.tag).toBe('h1')
61
+ })
@@ -0,0 +1,13 @@
1
+ 'use strict'
2
+
3
+ import { create } from '../../src/element'
4
+
5
+ const element = create({})
6
+
7
+ test('should SET element', () => {
8
+ element.set({ text: 'test' })
9
+ expect(element.content.text).toBe('test')
10
+
11
+ element.set({ text: 'test2' })
12
+ expect(element.content.text).toBe('test2')
13
+ })
@@ -0,0 +1,13 @@
1
+ 'use strict'
2
+
3
+ import { create } from '../../src/element'
4
+
5
+ const element = create({})
6
+
7
+ test('should UPDATE element', () => {
8
+ expect(element.text).toBeUndefined()
9
+
10
+ element.update('test')
11
+
12
+ expect(element.text).toBe('test')
13
+ })
@@ -0,0 +1,112 @@
1
+ 'use strict'
2
+
3
+ import { exec, isArray, isFunction, isObject, isString, overwriteDeep } from '@domql/utils'
4
+ import { applyExtend } from '../extend'
5
+ const ENV = process.env.NODE_ENV
6
+
7
+ export const checkIfKeyIsComponent = (key) => {
8
+ const isFirstKeyString = isString(key)
9
+ if (!isFirstKeyString) return
10
+ const firstCharKey = key.slice(0, 1)
11
+ return /^[A-Z]*$/.test(firstCharKey)
12
+ }
13
+
14
+ export const addAdditionalExtend = (newExtend, element) => {
15
+ const { extend } = element
16
+ const preserveExtend = isArray(extend) ? extend : [extend]
17
+ return {
18
+ ...element,
19
+ extend: [newExtend].concat(preserveExtend)
20
+ }
21
+ }
22
+
23
+ export const extendizeByKey = (element, parent, key) => {
24
+ const { tag, extend, props, state, childExtend, childProps, on } = element
25
+ const hasComponentAttrs = tag || extend || childExtend || props || state || on
26
+ const componentKey = key.includes('_')
27
+ ? key.split('_')[0]
28
+ : key.includes('.') ? key.split('.')[0] : key
29
+ const extendKey = componentKey || key
30
+
31
+ if (!hasComponentAttrs || childProps) {
32
+ return {
33
+ extend: extendKey,
34
+ props: { ...element }
35
+ }
36
+ } else if (!extend || extend === true) {
37
+ return {
38
+ ...element,
39
+ extend: extendKey
40
+ }
41
+ } else if (extend) {
42
+ addAdditionalExtend(extendKey, element)
43
+ } else if (isFunction(element)) {
44
+ return {
45
+ extend: extendKey,
46
+ props: { ...exec(element, parent) }
47
+ }
48
+ }
49
+ }
50
+
51
+ export const applyKeyComponentAsExtend = (element, parent, key) => {
52
+ return extendizeByKey(element, parent, key) || element
53
+ }
54
+
55
+ export const applyComponentFromContext = (element, parent, options) => {
56
+ const { context } = element
57
+ const { components } = context
58
+ const { extend } = element
59
+ const execExtend = exec(extend, element)
60
+ if (isString(execExtend)) {
61
+ if (components[execExtend]) element.extend = components[execExtend]
62
+ else {
63
+ if ((ENV === 'test' || ENV === 'development') && options.verbose) {
64
+ console.warn(execExtend, 'is not in library', components, element)
65
+ console.warn('replacing with ', {})
66
+ }
67
+ element.extend = {}
68
+ }
69
+ }
70
+ }
71
+
72
+ export const isVariant = (param) => {
73
+ if (!isString(param)) return
74
+ const firstCharKey = param.slice(0, 1)
75
+ // return (firstCharKey === '.' || firstCharKey === '$')
76
+ return (firstCharKey === '.')
77
+ }
78
+
79
+ export const hasVariantProp = (element) => {
80
+ const { props } = element
81
+ if (isObject(props) && isString(props.variant)) return true
82
+ }
83
+
84
+ export const overwriteVariant = (element, variant, variantProps) => {
85
+ let variantElement = element[variant]
86
+ if (!variantElement) return
87
+ const props = isObject(variantProps) ? variantProps : {}
88
+ if (isString(variantElement)) {
89
+ variantElement = {
90
+ extend: [{ props }, variantElement]
91
+ }
92
+ } else if (variantElement.extend) {
93
+ variantElement = addAdditionalExtend({ props }, variantElement)
94
+ }
95
+ return overwriteDeep(element, applyExtend(variantElement)) // TODO: check why string is not working
96
+ }
97
+
98
+ export const applyVariant = (element) => {
99
+ const { props } = element
100
+ if (!hasVariantProp(element)) return element
101
+ const { variant } = props
102
+ overwriteVariant(element, `.${variant}`)
103
+
104
+ const elKeys = Object.keys(element).filter((key) => isVariant(key))
105
+ elKeys.forEach((variant) => {
106
+ const slicedVariantElementKey = variant.slice(1)
107
+ const variantElementProps = props[slicedVariantElementKey]
108
+ if (variantElementProps) overwriteVariant(element, variant, variantElementProps)
109
+ })
110
+
111
+ return element
112
+ }
@@ -0,0 +1,118 @@
1
+ 'use strict'
2
+
3
+ import { isArray, isFunction, isObject, isString } from '@domql/utils'
4
+
5
+ export const generateHash = () => Math.random().toString(36).substring(2)
6
+
7
+ // hashing
8
+ export const extendStackRegistry = {}
9
+ export const extendCachedRegistry = {}
10
+
11
+ export const getHashedExtend = extend => {
12
+ return extendStackRegistry[extend.__hash]
13
+ }
14
+
15
+ export const setHashedExtend = (extend, stack) => {
16
+ const hash = generateHash()
17
+ if (!isString(extend)) { extend.__hash = hash }
18
+ extendStackRegistry[hash] = stack
19
+ return stack
20
+ }
21
+
22
+ export const getExtendStackRegistry = (extend, stack) => {
23
+ if (extend.__hash) { return stack.concat(getHashedExtend(extend)) }
24
+ return setHashedExtend(extend, stack) // stack .concat(hashedExtend)
25
+ }
26
+
27
+ // stacking
28
+ export const extractArrayExtend = (extend, stack) => {
29
+ extend.forEach(each => flattenExtend(each, stack))
30
+ return stack
31
+ }
32
+
33
+ export const deepExtend = (extend, stack) => {
34
+ const extendOflattenExtend = extend.extend
35
+ if (extendOflattenExtend) {
36
+ flattenExtend(extendOflattenExtend, stack)
37
+ }
38
+ return stack
39
+ }
40
+
41
+ export const flattenExtend = (extend, stack) => {
42
+ if (!extend) return stack
43
+ if (isArray(extend)) return extractArrayExtend(extend, stack)
44
+ stack.push(extend)
45
+ if (extend.extend) deepExtend(extend, stack)
46
+ return stack
47
+ }
48
+
49
+ // merging
50
+ export const deepCloneExtend = obj => {
51
+ const o = {}
52
+ for (const prop in obj) {
53
+ if (['parent', 'node', '__element'].indexOf(prop) > -1) continue
54
+ const objProp = obj[prop]
55
+ if (isObject(objProp)) {
56
+ o[prop] = deepCloneExtend(objProp)
57
+ } else if (isArray(objProp)) {
58
+ o[prop] = objProp.map(x => x)
59
+ } else o[prop] = objProp
60
+ }
61
+ return o
62
+ }
63
+
64
+ export const deepMergeExtend = (element, extend) => {
65
+ for (const e in extend) {
66
+ if (['parent', 'node', '__element'].indexOf(e) > -1) continue
67
+ const elementProp = element[e]
68
+ const extendProp = extend[e]
69
+ if (elementProp === undefined) {
70
+ element[e] = extendProp
71
+ } else if (isObject(elementProp) && isObject(extendProp)) {
72
+ deepMergeExtend(elementProp, extendProp)
73
+ } else if (isArray(elementProp) && isArray(extendProp)) {
74
+ element[e] = elementProp.concat(extendProp)
75
+ } else if (isArray(elementProp) && isObject(extendProp)) {
76
+ const obj = deepMergeExtend({}, elementProp)
77
+ element[e] = deepMergeExtend(obj, extendProp)
78
+ } else if (elementProp === undefined && isFunction(extendProp)) {
79
+ element[e] = extendProp
80
+ }
81
+ }
82
+ return element
83
+ }
84
+
85
+ export const cloneAndMergeArrayExtend = stack => {
86
+ return stack.reduce((a, c) => {
87
+ return deepMergeExtend(a, deepCloneExtend(c))
88
+ }, {})
89
+ }
90
+
91
+ export const replaceStringsWithComponents = (stack, components) => {
92
+ return stack.map(v => {
93
+ if (isString(v)) return components[v]
94
+ else return v
95
+ })
96
+ }
97
+
98
+ // joint stacks
99
+ export const jointStacks = (extendStack, childExtendStack) => {
100
+ return []
101
+ .concat(extendStack.slice(0, 1))
102
+ .concat(childExtendStack.slice(0, 1))
103
+ .concat(extendStack.slice(1))
104
+ .concat(childExtendStack.slice(1))
105
+ }
106
+
107
+ // init
108
+ export const getExtendStack = extend => {
109
+ if (!extend) return []
110
+ if (extend.__hash) return getHashedExtend(extend) || []
111
+ const stack = flattenExtend(extend, [])
112
+ return getExtendStackRegistry(extend, stack)
113
+ }
114
+
115
+ export const getExtendMerged = extend => {
116
+ const stack = getExtendStack(extend)
117
+ return cloneAndMergeArrayExtend(stack)
118
+ }
package/utils/index.js ADDED
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ export * from './object'
4
+ export * from './extendUtils'
5
+ export * from './component'
@@ -0,0 +1,141 @@
1
+ 'use strict'
2
+
3
+ import { isArray, isObject, isObjectLike, joinArrays } from '@domql/utils'
4
+ import { IGNORE_STATE_PARAMS } from '@domql/state'
5
+ import { IGNORE_PROPS_PARAMS } from '../props'
6
+ import { METHODS } from '../methods'
7
+
8
+ export const METHODS_EXL = joinArrays(
9
+ ['node', 'state', 'context', 'extend'],
10
+ METHODS,
11
+ IGNORE_STATE_PARAMS,
12
+ IGNORE_PROPS_PARAMS
13
+ )
14
+
15
+ export const deepMerge = (element, extend, exclude = METHODS_EXL) => {
16
+ for (const e in extend) {
17
+ if (exclude.includes(e)) continue
18
+ const elementProp = element[e]
19
+ const extendProp = extend[e]
20
+ if (elementProp === undefined) {
21
+ element[e] = extendProp
22
+ } else if (isObjectLike(elementProp) && isObject(extendProp)) {
23
+ deepMerge(elementProp, extendProp)
24
+ }
25
+ }
26
+ return element
27
+ }
28
+
29
+ export const clone = (obj, exclude = METHODS_EXL) => {
30
+ const o = {}
31
+ for (const e in obj) {
32
+ if (exclude.includes(e)) continue
33
+ o[e] = obj[e]
34
+ }
35
+ return o
36
+ }
37
+
38
+ /**
39
+ * Deep cloning of object
40
+ */
41
+ export const deepClone = (obj, exclude = METHODS_EXL) => {
42
+ const o = isArray(obj) ? [] : {}
43
+ for (const e in obj) {
44
+ if (exclude.includes(e)) continue
45
+ let objProp = obj[e]
46
+ if (e === 'extend' && isArray(objProp)) {
47
+ objProp = mergeArray(objProp, exclude)
48
+ }
49
+ if (isObjectLike(objProp)) {
50
+ o[e] = deepClone(objProp, exclude)
51
+ } else o[e] = objProp
52
+ }
53
+ return o
54
+ }
55
+
56
+ /**
57
+ * Overwrites object properties with another
58
+ */
59
+ export const overwrite = (element, params, options) => {
60
+ const changes = {}
61
+ const { __ref } = element
62
+ const { __exec, __cached } = __ref
63
+
64
+ for (const e in params) {
65
+ if (e === 'props' || e === 'state' || e === '__ref') continue
66
+
67
+ const elementProp = element[e]
68
+ const paramsProp = params[e]
69
+
70
+ if (paramsProp !== undefined) {
71
+ __cached[e] = changes[e] = elementProp
72
+ element[e] = paramsProp
73
+ }
74
+
75
+ if (options.cleanExec) delete __exec[e]
76
+ }
77
+
78
+ return changes
79
+ }
80
+
81
+ export const overwriteShallow = (obj, params, exclude = METHODS_EXL) => {
82
+ for (const e in params) {
83
+ if (exclude.includes(e)) continue
84
+ obj[e] = params[e]
85
+ }
86
+ return obj
87
+ }
88
+
89
+ /**
90
+ * Overwrites DEEPly object properties with another
91
+ */
92
+ export const overwriteDeep = (obj, params, exclude = METHODS_EXL) => {
93
+ for (const e in params) {
94
+ if (exclude.includes(e)) continue
95
+ const objProp = obj[e]
96
+ const paramsProp = params[e]
97
+ if (isObjectLike(objProp) && isObjectLike(paramsProp)) {
98
+ overwriteDeep(objProp, paramsProp)
99
+ } else if (paramsProp !== undefined) {
100
+ obj[e] = paramsProp
101
+ }
102
+ }
103
+ return obj
104
+ }
105
+
106
+ /**
107
+ * Overwrites object properties with another
108
+ */
109
+ export const mergeIfExisted = (a, b) => {
110
+ if (isObjectLike(a) && isObjectLike(b)) return deepMerge(a, b)
111
+ return a || b
112
+ }
113
+
114
+ /**
115
+ * Merges array extends
116
+ */
117
+ export const mergeArray = (arr, exclude = ['parent', 'node', '__element', 'state', 'context', '__ref']) => {
118
+ return arr.reduce((a, c) => deepMerge(a, deepClone(c, exclude)), {})
119
+ }
120
+
121
+ /**
122
+ * Merges array extends
123
+ */
124
+ export const mergeAndCloneIfArray = obj => {
125
+ return isArray(obj) ? mergeArray(obj) : deepClone(obj)
126
+ }
127
+
128
+ /**
129
+ * Overwrites object properties with another
130
+ */
131
+ export const flattenRecursive = (param, prop, stack = []) => {
132
+ const objectized = mergeAndCloneIfArray(param)
133
+ stack.push(objectized)
134
+
135
+ const extendOfExtend = objectized[prop]
136
+ if (extendOfExtend) flattenRecursive(extendOfExtend, prop, stack)
137
+
138
+ delete objectized[prop]
139
+
140
+ return stack
141
+ }