@lexriver/dome 2.0.2 → 2.0.3

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/src/Dome.mts DELETED
@@ -1,346 +0,0 @@
1
- // inspiration: https://github.com/vadimdemedes/dom-chef/blob/master/index.js
2
- const svgTagNames = require('svg-tag-names')
3
- import { DataTypes } from '@lexriver/data-types'
4
- import { ObservableVariable, checkIfObservable } from '@lexriver/observable'
5
- import { DomeComponent } from './DomeComponent.mjs'
6
- import { DomeManipulator } from './DomeManipulator.mjs'
7
-
8
- const filename = '[Dome]: '
9
-
10
-
11
- interface OnEventObject{
12
- eventName:string
13
- eventParams:Object
14
- eventReaction:(eventParams:Object)=>void
15
- }
16
-
17
-
18
-
19
- //const mountFunctionByNode = new Map<Node, (ref:any)=>void>()
20
-
21
- // https://github.com/jonschlinkert/arr-flatten/blob/master/index.js
22
- function flattenArray(arr:any[], res:any[] = []){
23
- var i = 0, cur;
24
- var len = arr.length;
25
- for (; i < len; i++) {
26
- cur = arr[i];
27
- Array.isArray(cur) ? flattenArray(cur, res) : res.push(cur);
28
- }
29
- return res;
30
- }
31
-
32
-
33
- function checkIfNonDimensionalCssName(name:string){
34
- // Copied from Preact
35
- const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i
36
- return IS_NON_DIMENSIONAL.test(name)
37
- }
38
-
39
-
40
- const excludeSvgTags = [
41
- 'a',
42
- 'audio',
43
- 'canvas',
44
- 'iframe',
45
- 'script',
46
- 'video'
47
- ]
48
-
49
- const svgTags = svgTagNames.filter(name => !excludeSvgTags.includes(name))
50
-
51
- const isSVG = tagName => svgTags.includes(tagName)
52
-
53
- const setCSSProps = (el, style:{[key:string]:string|number}) => {
54
- if(DataTypes.isString(style)){
55
- el.style = style
56
-
57
- } else if(DataTypes.isObjectWithKeys(style)){
58
- Object.keys(style).forEach(name => {
59
- let value = style[name]
60
-
61
- if (typeof value === 'number' && !checkIfNonDimensionalCssName(name)) {
62
- value = `${value}px`
63
- }
64
-
65
- el.style[name] = value
66
- })
67
- }
68
- }
69
-
70
- const createElement = (tagName) => {
71
- if (isSVG(tagName)) {
72
- return document.createElementNS('http://www.w3.org/2000/svg', tagName)
73
- }
74
-
75
- if (tagName === DocumentFragment) {
76
- return document.createDocumentFragment()
77
- }
78
-
79
- return document.createElement(tagName)
80
- }
81
-
82
-
83
- // function subscribeToEventByParam(x:OnEventObject, el:HTMLElement){
84
- // if(
85
- // !x.eventName ||
86
- // Data.isString(x.eventName) == false ||
87
- // !x.eventReaction ||
88
- // Data.isFunction(x.eventReaction) == false
89
- // ){
90
- // console.error('x=', x, 'el=', el)
91
- // throw new Error(`Please provide object of type {eventName:string, eventParams:Object, eventReaction:(eventParams, htmlElement)=>void}`)
92
- // }
93
- // //DomeEventDispatcher.subscribeToEvent(el, x.eventName, x.eventParams || {}, x.eventReaction)
94
- // DomeEventDispatcher.subscribeToEvent({
95
- // eventName: x.eventName,
96
- // eventParamsFilter: x.eventParams || {},
97
- // action: x.eventReaction,
98
- // unsubscribeWhen: () => DomeManipulator.isInDom(el) == false
99
- // })
100
-
101
-
102
- // }
103
-
104
- const build = (tagName, attrs, children:DocumentFragment) => {
105
- if(!tagName) {
106
- console.trace()
107
- throw new Error('tagName='+tagName)
108
- }
109
- // if(children && typeof children !== 'object'){
110
- // //console.log('tagName=', tagName, 'attrs=',attrs, 'children=', children)
111
- // console.log('tagName=', tagName, 'children=', children, 'typeof children=', typeof children)
112
- // }
113
- if(tagName == DocumentFragment){
114
- //console.warn(filename, 'isDocumentFragment!')
115
- const el = createElement(tagName)
116
- el.appendChild(children)
117
- return el
118
- }
119
-
120
- //console.log(filename, 'instaceof=', tagName instanceof DomeComponent)
121
- //console.log(filename, 'has render=', tagName.render)
122
- //console.log(filename, 'instanceof Object=', tagName instanceof Object)
123
-
124
- if(tagName.prototype && tagName.prototype['__DomeComponent']){
125
- // we have a DomeComponent
126
- //console.warn('DomeComponent!')
127
- try {
128
- const instance = new tagName(attrs, children) as DomeComponent<any>
129
- //console.log('instance=', instance)
130
- if(instance['render'] && DataTypes.isFunction(instance['render'])){
131
-
132
-
133
- //#region make ref points to instance, not element
134
- if(attrs.ref && DataTypes.isFunction(attrs.ref)){
135
- attrs.ref(instance)
136
- }
137
- //#endregion
138
-
139
- instance['init']()
140
-
141
- instance.rootElement = instance['render']()
142
-
143
- //#region subscribe to observable attribute
144
- for(let attribute of Object.values(attrs)){
145
- if(checkIfObservable(attribute)){
146
- attribute.eventOnChange.subscribe(() => {
147
- //this.update()
148
- instance.scheduleUpdate()
149
- })
150
- }
151
- }
152
- //#endregion
153
-
154
-
155
- //requestAnimationFrame(() => {
156
- instance['afterRender']()
157
- //})
158
-
159
- return instance.rootElement
160
- }
161
- } catch(error){
162
- console.error('error while creating DomeComponent class', error)
163
- }
164
- return;
165
-
166
- }
167
- if(DataTypes.isFunction(tagName)){
168
- // call functional component
169
- return tagName(attrs, children)
170
-
171
- } else if(DataTypes.isString(tagName)){
172
- const el = createElement(tagName)
173
- //console.log('lex-dome', 'creating element', el)
174
-
175
- Object.keys(attrs).forEach(name => {
176
- const value = attrs[name]
177
-
178
- if (name === 'class' || name === 'className' || name === 'cssClasses') {
179
- //DomeManipulator.setCssClasses(el, value)
180
- assignDynamicCssClasses(name, value, el)
181
-
182
-
183
-
184
- } else if (name === 'style') {
185
- setCSSProps(el, value)
186
-
187
- } else if(['disabled', 'autocomplete', 'selected', 'checked'].indexOf(name) >= 0){
188
- if(attrs[name]){
189
- DomeManipulator.setAttribute(el, name, name)
190
- }
191
-
192
- } else if(name === 'onCreate' || name === 'ref'){
193
- if(DataTypes.isFunction(value) == false) throw new Error(`Please provide function <${tagName} ${name}={ref => myRef=ref} />`)
194
- value(el)
195
-
196
- // } else if(name === 'onEvent'){ //TODO: remove this!?
197
- // // <div onEvent={{eventName:'myCumstomEvent', eventParams:{any:'params',can:'be',here:true}, eventReaction:()=>{}}}>some text for div</div>
198
- // //#region is Object
199
- // if(Data.isObjectWithKeys(value)){
200
- // subscribeToEventByParam(value as OnEventObject, el)
201
- // return
202
- // }
203
- // //#endregion
204
-
205
- // //#region is Array
206
- // if(Data.isArray(value)){
207
- // for(let x of value){
208
- // subscribeToEventByParam(x as OnEventObject, el)
209
- // }
210
- // }
211
- // //#endregion
212
-
213
- } else if(name == 'visibleIf'){ //TODO: remove this!?
214
- if(checkIfObservable(value) == false) {
215
- console.error('value=', value)
216
- throw new Error('Please provide Observable<boolean> as argument for visibleIf')
217
- }
218
- let obs = value as ObservableVariable<boolean>
219
- obs.eventOnChange.subscribe((isVisible) => {
220
- if(isVisible){
221
- DomeManipulator.unhideElementAsync(el)
222
- } else {
223
- DomeManipulator.hideElementAsync(el)
224
- }
225
- })
226
-
227
- setTimeout(() => { //TODO: hack to trigger event on mount to DOM
228
- obs.eventOnChange.triggerAsync(obs.get())
229
- }, 1)
230
-
231
- } else if (name.indexOf('on') === 0 && value) {
232
- const eventName = name.slice(2).toLowerCase()
233
- if(DataTypes.isFunction(value) == false) {
234
- console.error('unable to subscribe for event', eventName, 'listener is not a function', 'element=', el, 'listener=', value)
235
- return
236
- }
237
- //console.log('adding event listener for', el, 'eventName=', eventName, 'value=', value)
238
- el.addEventListener(eventName, value)
239
-
240
- } else if (name === 'innerHtml') {
241
- el.innerHTML = value
242
-
243
- } else if (name !== 'key' && value !== false) {
244
- DomeManipulator.setAttribute(el, name, value === true ? '' : value)
245
- }
246
- })
247
-
248
- if (!attrs.innerHtml) {
249
- el.appendChild(children)
250
- }
251
-
252
- return el
253
-
254
- } else throw new Error("not implemented")
255
- }
256
-
257
-
258
-
259
- /**
260
- *
261
- * @param value
262
- * @param name
263
- * @param element
264
- */
265
- function assignDynamicCssClasses(name: string, value: {[key:string]:boolean|ObservableVariable<boolean>}, element: any) {
266
- if (DataTypes.isObjectWithKeys(value) == false){
267
- DomeManipulator.setCssClasses(element, value)
268
- return
269
- //throw new Error(`Please provide object for ${name}, ex: {class1:true, class2:myVarO}`)
270
- }
271
- let resultArray: string[] = []
272
- for (let [k, v] of Object.entries(value)) {
273
- if(v === undefined){
274
- //skip
275
-
276
- } else if (DataTypes.isBoolean(v)) {
277
- if(v){
278
- resultArray.push(k)
279
- }
280
-
281
- } else if (checkIfObservable(v)) {
282
- let o = v as ObservableVariable<boolean>
283
- o.eventOnChange.subscribe((showThiCssClass) => {
284
- if(!DomeManipulator.isInDom(element)) return {unsubscribe:true} //TODO: test it
285
- // reassign whole attribute
286
- DomeManipulator.setCssClasses(element, value)
287
- })
288
- if (o.get()) {
289
- resultArray.push(k)
290
- }
291
- } else {
292
- console.error(`Please provide classNames as a keys and boolean or Observable<boolean> for values., ex: {class1:true, class2:myVarO}`, 'name=', name, 'value=', value, 'typeof value =', typeof value)
293
- throw new Error('Wrong value for `class` attribute')
294
- }
295
- }
296
- DomeManipulator.setCssClasses(element, resultArray) // yes, array of classes is ok here
297
- }
298
-
299
- export function h(tagName, attrs, ...childrenArgs) {
300
- // eslint-disable-next-line prefer-rest-params
301
- //const childrenArgs = [].slice.apply(arguments, [2])
302
- const children = document.createDocumentFragment()
303
- //const childArray:any[] = []
304
-
305
- // console.log(filename, 'childrenArgs=', childrenArgs)
306
- // console.log(filename, 'childrenO=', childrenO)
307
- // if(tagName === 'hoho'){
308
- // console.warn(filename, tagName, attrs, ...childrenArgs)
309
- // }
310
-
311
- flattenArray(childrenArgs).forEach(child => {
312
- //(childrenArgs).forEach(child => {
313
- // if(tagName === 'hoho'){
314
- // console.warn(filename, 'child=', child)
315
- // }
316
- if (child instanceof Node) {
317
- children.appendChild(child)
318
- //childArray.push(child)
319
-
320
- // const mountFunction = mountFunctionByNode.get(child)
321
- // if(mountFunction){
322
- // mountFunction(child)
323
- // mountFunctionByNode.delete(child)// delete after execution
324
- // }
325
-
326
- } else if (typeof child !== 'boolean' && typeof child !== 'undefined' && child !== null) {
327
- //console.log('[Dome] child=', child)
328
- //console.warn(filename, 'text? child=', child)
329
- children.appendChild(document.createTextNode(child))
330
- //childArray.push(document.createTextNode(child))
331
- }
332
- })
333
-
334
- //return build(tagName, attrs || {}, children)
335
- return build(tagName, attrs || {}, children)
336
- }
337
-
338
- // Improve TypeScript support for DocumentFragment
339
- // https://github.com/Microsoft/TypeScript/issues/20469
340
- export const React = {
341
- createElement: h,
342
- Fragment: typeof DocumentFragment === 'function' ? DocumentFragment : () => {}
343
- }
344
-
345
- export default React
346
-
@@ -1,84 +0,0 @@
1
- import { debounce } from 'ts-debounce'
2
- import { Animation } from './Animation.mjs'
3
- import { DomeManipulator } from "./DomeManipulator.mjs"
4
-
5
-
6
- interface InternalAttrs{
7
- ref?:(ref)=>void
8
- onShowAnimation?:Animation
9
- onHideAnimation?:Animation
10
- }
11
- export abstract class DomeComponent<Attrs>{
12
- public rootElement!:Element|HTMLElement
13
- private updateInProgress = false
14
- private updateRequested = false
15
- constructor(
16
- public attrs:Attrs & InternalAttrs,
17
- public children:any
18
- ){
19
- //this.init()
20
-
21
- }
22
- //abstract render(attrs:Attrs, children:any):HTMLElement
23
- protected init():void{}
24
- protected abstract render():HTMLElement
25
- protected afterRender():void{
26
- //console.log('afterRender(), el=', this.el)
27
- }
28
- // protected onMount(){
29
-
30
- // }
31
-
32
- async updateAsync(){
33
- //console.log('DomeComponent: calling native update() method!') //TODO: remove this
34
- if(!this.rootElement) {
35
- console.error('DomeComponent: unable to update, no rootElement', 'this=', this)
36
- return
37
- }
38
- if(!this.rootElement.parentNode){
39
- console.error('DomeComponent: unable to update, no parent for rootElement, not mounted?', 'this=', this)
40
- return
41
- }
42
-
43
- //console.time('browser')
44
- const newEl = this.render()
45
- //console.log('DomeComponent: update() newEl=', newEl) //TODO: remove this
46
- //DomeManipulator.unhideElement(this.el) // if was null before
47
- await DomeManipulator.replaceAsync(this.rootElement, newEl, this.attrs.onHideAnimation, this.attrs.onShowAnimation)
48
- this.rootElement = newEl
49
- //console.timeEnd('browser')
50
- this.afterUpdate()
51
-
52
- }
53
- private async runScheduledUpdateAsync() {
54
- if(this.updateInProgress){
55
- this.updateRequested = true
56
- return
57
- }
58
-
59
- this.updateInProgress = true
60
- try {
61
- do {
62
- this.updateRequested = false
63
- try {
64
- await this.updateAsync()
65
- } catch(error) {
66
- console.error('DomeComponent: scheduled update failed', error)
67
- }
68
- } while(this.updateRequested)
69
- } finally {
70
- this.updateInProgress = false
71
- }
72
- }
73
-
74
- scheduleUpdate = debounce(() => this.runScheduledUpdateAsync(), 5)
75
-
76
- protected afterUpdate(){
77
-
78
- }
79
- }
80
- DomeComponent.prototype['__DomeComponent'] = true
81
-
82
- // export interface DomeComponent<Attrs>{
83
- // render:(attrs:Attrs)=>HTMLElement
84
- // }