@lexriver/dome 2.0.1 → 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.
Files changed (43) hide show
  1. package/out/index.cjs +802 -0
  2. package/out/index.mjs +1987 -0
  3. package/out/src/AnimatedArray.d.ts +29 -0
  4. package/out/src/AnimatedTable.d.mts +2 -1
  5. package/out/src/AnimatedTable.d.ts +39 -0
  6. package/out/src/AnimatedTable.mjs +2 -1
  7. package/out/src/AnimatedText.d.mts +1 -1
  8. package/out/src/AnimatedText.d.ts +13 -0
  9. package/out/src/AnimatedText.mjs +1 -1
  10. package/out/src/Animation.d.ts +4 -0
  11. package/out/src/Dome.d.ts +9 -0
  12. package/out/src/Dome.mjs +1 -1
  13. package/out/src/DomeComponent.d.mts +4 -1
  14. package/out/src/DomeComponent.d.ts +25 -0
  15. package/out/src/DomeComponent.mjs +24 -3
  16. package/out/src/DomeManipulator.d.mts +4 -0
  17. package/out/src/DomeManipulator.d.ts +52 -0
  18. package/out/src/DomeManipulator.mjs +24 -13
  19. package/out/src/DomeRouter.console-test.d.ts +1 -0
  20. package/out/src/DomeRouter.d.ts +32 -0
  21. package/out/src/LongestCommonSubsequence.d.ts +15 -0
  22. package/out/src/concurrent-updates.test.d.mts +1 -0
  23. package/out/src/concurrent-updates.test.d.ts +1 -0
  24. package/out/src/concurrent-updates.test.mjs +203 -0
  25. package/out/src/index.d.ts +11 -0
  26. package/out/src/temp-test.d.ts +1 -0
  27. package/package.json +20 -3
  28. package/out/vitest.config.d.ts +0 -2
  29. package/out/vitest.config.js +0 -9
  30. package/src/AnimatedArray.mts +0 -74
  31. package/src/AnimatedTable.mts +0 -120
  32. package/src/AnimatedText.mts +0 -30
  33. package/src/Animation.mts +0 -4
  34. package/src/Dome.mts +0 -346
  35. package/src/DomeComponent.mts +0 -63
  36. package/src/DomeManipulator.mts +0 -380
  37. package/src/DomeRouter.console-test.mts +0 -52
  38. package/src/DomeRouter.mts +0 -287
  39. package/src/LongestCommonSubsequence.mts +0 -201
  40. package/src/index.mts +0 -12
  41. package/src/temp-test.mts +0 -23
  42. package/tsconfig.json +0 -59
  43. package/vitest.config.ts +0 -10
@@ -1,63 +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
- constructor(
14
- public attrs:Attrs & InternalAttrs,
15
- public children:any
16
- ){
17
- //this.init()
18
-
19
- }
20
- //abstract render(attrs:Attrs, children:any):HTMLElement
21
- protected init():void{}
22
- protected abstract render():HTMLElement
23
- protected afterRender():void{
24
- //console.log('afterRender(), el=', this.el)
25
- }
26
- // protected onMount(){
27
-
28
- // }
29
-
30
- async updateAsync(){
31
- //console.log('DomeComponent: calling native update() method!') //TODO: remove this
32
- if(!this.rootElement) {
33
- console.error('DomeComponent: unable to update, no rootElement', 'this=', this)
34
- return
35
- }
36
- if(!this.rootElement.parentNode){
37
- console.error('DomeComponent: unable to update, no parent for rootElement, not mounted?', 'this=', this)
38
- return
39
- }
40
-
41
- //console.time('browser')
42
- const newEl = this.render()
43
- //console.log('DomeComponent: update() newEl=', newEl) //TODO: remove this
44
- //DomeManipulator.unhideElement(this.el) // if was null before
45
- await DomeManipulator.replaceAsync(this.rootElement, newEl, this.attrs.onHideAnimation, this.attrs.onShowAnimation)
46
- this.rootElement = newEl
47
- //console.timeEnd('browser')
48
- this.afterUpdate()
49
-
50
- }
51
- scheduleUpdate = debounce(() => {
52
- this.updateAsync()
53
- }, 5)
54
-
55
- protected afterUpdate(){
56
-
57
- }
58
- }
59
- DomeComponent.prototype['__DomeComponent'] = true
60
-
61
- // export interface DomeComponent<Attrs>{
62
- // render:(attrs:Attrs)=>HTMLElement
63
- // }
@@ -1,380 +0,0 @@
1
- import { Async } from "@lexriver/async"
2
- import { DataTypes } from "@lexriver/data-types"
3
- import { ObservableVariable, checkIfObservable } from "@lexriver/observable"
4
- import { Animation } from './Animation.mjs'
5
-
6
-
7
- export type CssClass = {[key:string]:boolean|ObservableVariable<boolean>} | string[] | string
8
-
9
- export namespace DomeManipulator {
10
-
11
- export async function hideElementAsync(element: Element, animation?:Animation) {
12
- if(!element) throw new Error('hideElementAsync failed, no element')
13
- if((element as HTMLElement).hidden) return
14
- if(animation){
15
- await addCssClassAsync(element, animation.cssClassName, animation.timeMs)
16
- }
17
- (element as HTMLElement).style.display = 'none'; //TODO: remove this?
18
- (element as HTMLElement).hidden = true
19
-
20
- }
21
-
22
- export async function unhideElementAsync(element: Element, animation?:Animation) {
23
- if(!element) throw new Error('unhideElementAsync failed, no element')
24
- if(!(element as HTMLElement).hidden) return
25
- (element as HTMLElement).style.display = ''; //TODO: remove this?
26
- (element as HTMLElement).hidden = false
27
- if(animation){
28
- await addCssClassAsync(element, animation.cssClassName, animation.timeMs)
29
- }
30
- }
31
-
32
- export async function insertAsFirstChildAsync(elementToInsert: Element, parentElement: Element | DocumentFragment, animation?:Animation) {
33
- parentElement.insertBefore(elementToInsert, parentElement.firstChild)
34
- if(animation){
35
- await addCssClassAsync(elementToInsert, animation.cssClassName, animation.timeMs)
36
- }
37
-
38
- }
39
-
40
- export async function insertBeforeAsync(elementToInsert: Element, refElement: Element | null, parentElement: Element | DocumentFragment, animation?:Animation) {
41
- parentElement.insertBefore(elementToInsert, refElement)
42
- if(animation){
43
- await addCssClassAsync(elementToInsert, animation.cssClassName, animation.timeMs)
44
- }
45
-
46
- }
47
-
48
- export async function insertAfterAsync(elementToInsert: Element, refElement: Element | null | undefined, parentElement: Element | DocumentFragment, animation?:Animation) {
49
- if (refElement) {
50
- parentElement.insertBefore(elementToInsert, refElement.nextSibling)
51
- } else {
52
- parentElement.appendChild(elementToInsert)
53
- }
54
- if(animation){
55
- await addCssClassAsync(elementToInsert, animation.cssClassName, animation.timeMs)
56
- }
57
-
58
- }
59
-
60
- export async function insertByIndexAsync(elementToInsert: Element, index: number, parentElement: Element | DocumentFragment, animation?:Animation) {
61
- if (index == 0) {
62
- parentElement.appendChild(elementToInsert)
63
- if(animation){
64
- await addCssClassAsync(elementToInsert, animation.cssClassName, animation.timeMs)
65
- }
66
- return
67
- }
68
-
69
- await insertAfterAsync(elementToInsert, parentElement.children[index], parentElement, animation)
70
-
71
- }
72
-
73
-
74
- export async function replaceAsync(oldElement: Element, newElement: Element, animationHide?:Animation, animationShow?:Animation) {
75
- if(!oldElement.parentNode){
76
- // almost impossible if in DOM
77
- console.error('replaceAsync() failed, no parentNode.', 'oldElement=', oldElement)
78
- throw new Error('no parent node for replaceAsync')
79
- }
80
-
81
- // hide
82
- if(animationHide){
83
- await addCssClassAsync(oldElement, animationHide.cssClassName, animationHide.timeMs)
84
- }
85
-
86
- // replace
87
- const parent = oldElement.parentNode
88
- if (!parent) {
89
- //TODO: do not throw error here?
90
- console.error('replaceAsync() failed, no parent. Probably node was deleted while hide animation.', 'oldElement=', oldElement, 'type=', typeof oldElement, 'parent=', oldElement.parentElement, oldElement.parentNode)
91
- throw new Error('no parent for replaceAsync()')
92
- }
93
- parent.replaceChild(newElement, oldElement)
94
-
95
- // show
96
- if(animationShow){
97
- await addCssClassAsync(newElement, animationShow.cssClassName, animationShow.timeMs)
98
- }
99
-
100
- return newElement
101
-
102
- }
103
-
104
- export async function removeElementAsync(element: Element, animation?:Animation) {
105
- //console.log('#dome', 'removeELementAsync', element)
106
- if(animation){
107
- //console.log('#dome', 'add animation', animation.cssClassName, animation.timeMs, element)
108
- await addCssClassAsync(element, animation.cssClassName, animation.timeMs)
109
- }
110
- //console.log('#dome', 'removing from dom', element)
111
- element.remove()
112
- }
113
-
114
- export function forEachChildrenOf(element:Element, action:(child:ChildNode)=>void){
115
- for(let i=0;i<element.childNodes.length;i++){
116
- action(element.childNodes[i])
117
- }
118
- }
119
-
120
-
121
- export async function removeAllChildrenAsync(element: Element, animation?:Animation) {
122
- // await Promise.all( //TODO: this doesn't remove text nodes
123
- // Array.from(element.children).map(child => removeElementAsync(child, animation))
124
- // )
125
-
126
- if(!animation){
127
- while (element.firstChild) {
128
- //element.removeChild(element.firstChild);
129
- element.firstChild.remove()
130
- }
131
- //return
132
- } else {
133
- //console.log('##', 'assign remove animation for each children', element.childNodes)
134
- forEachChildrenOf(element, (child) => child.nodeType == Node.ELEMENT_NODE && (child as Element).classList.add(animation.cssClassName))
135
- //console.log('##', 'wait ms', animation.timeMs)
136
- await Async.waitMsAsync(animation.timeMs)
137
- //console.log('##', 'removing children', element.childNodes)
138
- forEachChildrenOf(element, (child) => child.remove())
139
- //console.log('##', 'done')
140
- }
141
-
142
- // if(element.children.length>0) {
143
- // console.error('DomeManipulator: removeAllChildrenAsync() failed', 'length=', element.children.length, 'children=', element.children, 'animation=', animation, 'element=', element)
144
- // throw new Error()
145
- // }
146
-
147
- }
148
-
149
- export async function appendChildAsync(containerElement:Element, child:Element, animation?:Animation){
150
- containerElement.appendChild(child)
151
- if(animation){
152
- await addCssClassAsync(child, animation.cssClassName, animation.timeMs)
153
- }
154
- }
155
-
156
- export async function appendChildrenAsync(containerElement:Element, children:Element | Element[] | DocumentFragment | Text | string | null | undefined, animation?:Animation){
157
- //console.log('DomeManipulator: appendChildrenAsync', 'containerELement=', containerElement, 'children=', children, 'animation=', animation)
158
- if (DataTypes.isString(children)) {
159
- // no animation for text?
160
- containerElement.appendChild(document.createTextNode(children as string))
161
-
162
- } else if (DataTypes.isArray(children)) {
163
- await Promise.all(
164
- (children as Element[]).map(child => appendChildAsync(containerElement, child, animation))
165
- )
166
-
167
- } else if (children) {
168
- await appendChildAsync(containerElement, children as Element, animation)
169
-
170
- }
171
-
172
- }
173
-
174
-
175
- export async function replaceAllChildrenAsync(
176
- containerElement: Element,
177
- childrenToInsert: Element | Element[] | DocumentFragment | Text | string | null | undefined,
178
- animationForHide?:Animation,
179
- animationForShow?:Animation
180
- ) {
181
- //await removeAllChildrenAsync(containerElement, animationForHide)
182
-
183
- if(containerElement.childNodes.length>0){
184
- await removeAllChildrenAsync(containerElement, animationForHide)
185
- }
186
-
187
- // while(containerElement.childNodes.length>0){
188
- // await removeAllChildrenAsync(containerElement, animationForHide) // can be executed more than once! TODO: why?
189
- // }
190
- // if(containerElement.children.length>0) {
191
- // console.error('DomeManipulator: replaceAllChildenrAsync() failed:', 'containerElement.children.length=', containerElement.children.length, 'children=', containerElement.children, 'containerElement=', containerElement, 'animationForHide=', animationForHide)
192
- // throw new Error()
193
- // }
194
- await appendChildrenAsync(containerElement, childrenToInsert, animationForShow)
195
- }
196
-
197
-
198
-
199
- export function isInDom(el: Element | undefined) {
200
- if (!el) return false
201
- return document.body.contains(el)
202
- }
203
-
204
- export function isOnScreen(el:Element|undefined){
205
- if(!el) return false
206
- var rect = el.getBoundingClientRect()
207
- var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight)
208
- return !(rect.bottom < 0 || rect.top - viewHeight >= 0)
209
- }
210
-
211
- export async function addCssClassAsync(element: Element, cssClassName: string, removeAfterMs?:number) {
212
- if(element.nodeType !== Node.ELEMENT_NODE) return
213
- element.classList.add(cssClassName)
214
- if(removeAfterMs && removeAfterMs>0){
215
- await Async.waitMsAsync(removeAfterMs)
216
- element.classList.remove(cssClassName)
217
- }
218
-
219
- }
220
-
221
- export async function addCssClassesAsync(element: Element, cssClassNames: string[], removeAfterMs?:number) {
222
- await Promise.all(cssClassNames.map(cssClassName => addCssClassAsync(element, cssClassName, removeAfterMs)))
223
- }
224
-
225
- export function removeCssClass(element: Element, cssClassName: string) {
226
- element.classList.remove(cssClassName)
227
- }
228
-
229
- export function removeCssClasses(element: Element, cssClassNames: string[]) {
230
- for (let cssClassName of cssClassNames) {
231
- element.classList.remove(cssClassName)
232
- }
233
-
234
-
235
- }
236
-
237
-
238
- /**
239
- * set css classes by array ['class1', 'class2'] or
240
- * by object {class1:true, class2:false} or
241
- * by Observable<boolean> {class1:true, class2:myVarO} or
242
- * by string
243
- * this function will replace class attribute so all classes that are not in params will be removed
244
- * @param value
245
- * @param element
246
- */
247
- export function setCssClasses(element: Element, value: CssClass) {
248
-
249
- if (!value) return // skip empty
250
- if (DataTypes.isArray(value)) {
251
- //console.log('setCssClasses', 'data is array', value)
252
- setAttribute(element, 'class', (value as string[]).join(' '))
253
-
254
- } else if (DataTypes.isObjectWithKeys(value)) {
255
- //console.log('setCssClasses', 'data is object with keys', value)
256
- let classNameArray: Array<string> = []
257
- for (let [k, v] of Object.entries(value)) {
258
- if (checkIfObservable(v)) {
259
- if ((v as ObservableVariable<boolean>).get()) {
260
- classNameArray.push(k)
261
- }
262
- } else if (v) {
263
- classNameArray.push(k)
264
- }
265
- }
266
- setAttribute(element, 'class', classNameArray.join(' '))
267
-
268
- } else {
269
- //console.log('setCssClasses', 'data is unknown', value)
270
- setAttribute(element, 'class', value)
271
-
272
- }
273
-
274
- }
275
-
276
- export function setAttribute(element: Element, name: string, value: any) {
277
- if (value === undefined || value === null) {
278
- return
279
- }
280
-
281
- // Naive support for xlink namespace
282
- // Full list: https://github.com/facebook/react/blob/1843f87/src/renderers/dom/shared/SVGDOMPropertyConfig.js#L258-L264
283
- if (/^xlink[AHRST]/.test(name)) {
284
- element.setAttributeNS('http://www.w3.org/1999/xlink', name.replace('xlink', 'xlink:').toLowerCase(), value)
285
- } else {
286
- element.setAttribute(name, value)
287
- }
288
- }
289
-
290
- export function hasFocus(el: Element):boolean {
291
- return document.activeElement == el
292
- }
293
-
294
- export function scrollIntoView(element: Element, paddingFromTop:number = 100) {
295
- if(isOnScreen(element)) {
296
- return // no need to scroll
297
- }
298
- //const positionBefore = getCurrentScrollPosition()
299
- //element.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
300
- //const positionAfter = getCurrentScrollPosition()
301
- //console.log('before=', positionBefore, 'after=', positionAfter)
302
- let expectedPosition = element.getBoundingClientRect().top + window.pageYOffset;
303
- expectedPosition -= paddingFromTop
304
- window.scrollTo({top: expectedPosition, behavior: 'smooth'});
305
-
306
-
307
-
308
- }
309
-
310
- // https://stackoverflow.com/questions/15935318/smooth-scroll-to-top/55926067
311
- export const scrollToTop = () => {
312
- //console.log('scrollToTop')
313
- let position = getCurrentScrollPosition()
314
-
315
- if (position > 0) {
316
- window.requestAnimationFrame(scrollToTop);
317
- if (position < 20) {
318
- window.scrollTo(0, 0)
319
- } else {
320
- window.scrollTo(0, position - position / 9)
321
- }
322
- }
323
- }
324
-
325
- export function getCurrentScrollPosition(){
326
- return document.documentElement.scrollTop || document.body.scrollTop;
327
- }
328
-
329
- export async function scrollToAsync(p:{
330
- pxFromTop?:number,
331
- pxFromLeft?:number,
332
- smooth?:boolean,
333
- msStep?:number,
334
- maxMsToWait?:number
335
- }){
336
- //console.log('scrollToAsync', p)
337
- const msStep = p.msStep ?? 50
338
- const maxMsToWait = p.maxMsToWait ?? 5000
339
-
340
- let scrollOptions:ScrollToOptions = {
341
- }
342
-
343
- if(p.pxFromTop){
344
- scrollOptions.top = p.pxFromTop
345
- }
346
- if(p.pxFromLeft){
347
- scrollOptions.left = p.pxFromLeft
348
- }
349
- if(p.smooth){
350
- scrollOptions.behavior = 'smooth'
351
- }
352
-
353
-
354
- try {
355
- if(p.pxFromLeft || p.pxFromTop){
356
- await Async.waitForFunctionToReturnTrueAsync(() => {
357
- if(p.pxFromTop){
358
- return document.body.scrollHeight >= p.pxFromTop
359
- }
360
- if(p.pxFromLeft){
361
- return document.body.scrollWidth >= p.pxFromLeft
362
- }
363
- return false
364
- }, msStep, maxMsToWait)
365
- }
366
-
367
- } catch(error){
368
- // ignore error
369
-
370
- }
371
- //console.log('scrollToY', pxFromTop, 'height=', document.body.clientHeight)
372
- //console.log('scrollToAsync now', scrollOptions)
373
- window.scrollTo(scrollOptions)
374
- }
375
-
376
-
377
-
378
-
379
-
380
- }
@@ -1,52 +0,0 @@
1
-
2
- // //import { DomeRouter } from "./DomeRouter"
3
- // function getRouteSlices(route:string){
4
- // return route.split('/').filter(x => x.length>0)
5
- // }
6
-
7
- // function checkUrlMatchRouteAndGetParameters(url:string, route:string, exactMatch:boolean = true):Map<string,string>|undefined{
8
- // // this function is purely for export
9
-
10
- // const urlSlices = getRouteSlices(url)
11
- // const routeSlices = getRouteSlices(route)
12
- // if(exactMatch && routeSlices.length !== urlSlices.length) return undefined
13
- // // all routeSlices must match for !exactMatch
14
- // const extractedParameters = new Map<string,string>()
15
- // for(let i=0;i<routeSlices.length;i++){
16
- // let cRouteSlice = routeSlices[i]
17
- // let cUrlSlice = urlSlices[i] // could be undefined
18
- // //console.info('\t\t cRouteSlice=', cRouteSlice, 'cUrlSlice=', cUrlSlice)
19
-
20
- // if(cRouteSlice.startsWith(':')){
21
- // // we have a param name
22
- // if(cUrlSlice === undefined){
23
- // return undefined
24
- // }
25
- // // we have some variable, save it
26
- // extractedParameters.set(cRouteSlice.substring(1), cUrlSlice)
27
-
28
- // } else {
29
- // // no param name
30
- // if(cRouteSlice !== cUrlSlice){
31
- // return undefined
32
- // }
33
- // }
34
- // }
35
- // return extractedParameters
36
- // }
37
-
38
-
39
- // DomeRouter.onRoute('/', true, ()=>{ console.log('exact /')})
40
- // DomeRouter.onRoute('/', false, ()=>{ console.log('notexact /')})
41
- // DomeRouter.onRoute('/test', true, ()=>{ console.log('exact /test')})
42
- // DomeRouter.onRoute('/test/:id', true, (p)=>{ console.log('exact /test/:id', 'params=', p)})
43
- // DomeRouter.onRoute('/test/:id', false, (p)=>{ console.log('notexact /test/:id', 'params=', p)})
44
- // DomeRouter.onRoute('/test', false, ()=>{ console.log('notexact /test')})
45
- // DomeRouter.onRoute('/test/:id/:id2', true, (p)=>{ console.log('exact /test/:id/:id2', 'params=', p)})
46
-
47
- // let testRoute = '//test/34/st'
48
- // console.log('** testing route:', testRoute)
49
- // DomeRouter.execute(testRoute)
50
- // //expect(!true).toBeTruthy()
51
-
52
- //console.log(checkUrlMatchRouteAndGetParameters('/get-product/123', '/get-product/:id/:id2', false))