@srfnstack/fntags 1.2.1 → 1.2.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/package.json +5 -5
- package/src/fntags.d.mts.map +1 -1
- package/src/fntags.mjs +56 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@srfnstack/fntags",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"author": "Robert Kempton <r@snow87.com>",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/srfnstack/fntags",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"typescript": "^5.3.3"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
|
-
"test": "cp src/*.mjs docs/lib/ && npm run lint && cypress run --spec \"test/**\" --headless -b chrome",
|
|
54
|
-
"cypress": "cypress run --headed --spec test/** -b chrome",
|
|
55
|
-
"lint": "standard --env browser src && standard --env browser --env jest --global Prism --global cy test",
|
|
56
|
-
"lint:fix": "standard --env browser --fix src && standard --env browser --env jest --global Prism --global cy --fix test",
|
|
53
|
+
"test": "cp src/*.mjs docs/lib/ && npm run lint && npm test --prefix packages/fntags-testing && cypress run --spec \"test/docs/**\" --headless -b chrome",
|
|
54
|
+
"cypress": "cypress run --headed --spec test/docs/** -b chrome",
|
|
55
|
+
"lint": "standard --env browser src && standard --env browser --env jest --global Prism --global cy test/docs",
|
|
56
|
+
"lint:fix": "standard --env browser --fix src && standard --env browser --env jest --global Prism --global cy --fix test/docs",
|
|
57
57
|
"typedef": "rm -rf src/*.mts* && tsc",
|
|
58
58
|
"docs": "typedoc --plugin typedoc-plugin-markdown --out docs/types --json docs/types.json ./src/*.mjs && node scripts/generateApi.js",
|
|
59
59
|
"generate-api": "node scripts/generateApi.js",
|
package/src/fntags.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fntags.d.mts","sourceRoot":"","sources":["fntags.mjs"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,2DALW,MAAM,eACH,CAAC,IAAI,MAAO,CAAC,OA6C1B;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,iEAPgB,GAAG,
|
|
1
|
+
{"version":3,"file":"fntags.d.mts","sourceRoot":"","sources":["fntags.mjs"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,2DALW,MAAM,eACH,CAAC,IAAI,MAAO,CAAC,OA6C1B;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,iEAPgB,GAAG,cAqIlB;AAqfD;;;;GAIG;AACH,iCAHW,GAAG,GACD,IAAI,CAuBhB;AAqFD;;;;GAIG;AACH,6BAHW,GAAG,GACD,OAAO,CAInB;AAED;;;;GAIG;AACH,mCAHW,GAAG,GACF,MAAM,CAIjB;AAED;;;;;;;;;;GAUG;AACH,kEALW,MAAM,GAAC,MAAM,OACb,MAAM,YACN,MAAM,EAAE,GAAC,IAAI,EAAE,KAezB;AAOD;;;;;;;;;;;;;;;GAeG;AACH,qDAJkB,IAAI,iBACH,IAAI,CA+DtB;;;;;;;;;kCAv4BmC,CAAC,YAAY,CAAC,KAAG,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;;;2BAE/C,CAAC,MAAI,CAAC,IAAI,GAAC,GAAG,CAAC,CAAC,GAAC,GAAG,GAAC,IAAI,gCAAkC,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;;qBAG9E,MAAM,KAAG,IAAI;;;;sCAEI,CAAC,YAAY,CAAC,KAAG,CAAC,MAAM,GAAC,GAAG,CAAC,KAAG,GAAG;;;;mCACvC,CAAC,YAAY,CAAC,KAAG,MAAM,KAAK,MAAM;;;;yCAC7B,GAAG,KAAG,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;+CACrB,GAAG,KAAG,CAAC,MAAM,GAAC,GAAG,CAAC,KAAG,GAAG;;;;;;kBAC7C,GAAG,KAAG,IAAI;;;;cAGhB,MAAK,GAAG;;;;;qBACC,CAAC,KAAG,IAAI;;;;;;oBAEV,MAAM,KAAG,GAAG;;;;oBAGZ,MAAM,SAAS,GAAG,mBAAmB,OAAO,KAAG,IAAI;;;;uCAClC,CAAC,YAAY,CAAC,KAAG,IAAI,KAAK,IAAI;;;;eACtD,OAAO;;;;;sDAKqB,CAAC,KAAG,CAAC"}
|
package/src/fntags.mjs
CHANGED
|
@@ -141,7 +141,8 @@ export function fnstate (initialValue, mapKey) {
|
|
|
141
141
|
} else {
|
|
142
142
|
const oldState = ctx.currentValue
|
|
143
143
|
ctx.currentValue = newState
|
|
144
|
-
|
|
144
|
+
const snapshot = ctx.observers.slice()
|
|
145
|
+
for (const observer of snapshot) {
|
|
145
146
|
observer.fn(newState, oldState)
|
|
146
147
|
}
|
|
147
148
|
}
|
|
@@ -277,11 +278,29 @@ function doSubscribe (callback) {
|
|
|
277
278
|
|
|
278
279
|
const subscribeSelect = (ctx, callback) => {
|
|
279
280
|
const parentCtx = ctx.state.parentCtx
|
|
280
|
-
const key = keyMapper(parentCtx.mapKey, ctx.currentValue)
|
|
281
|
+
const key = keyMapper(parentCtx.mapKey, ctx.currentValue, 0)
|
|
281
282
|
if (parentCtx.selectObservers[key] === undefined) {
|
|
282
283
|
parentCtx.selectObservers[key] = []
|
|
283
284
|
}
|
|
284
285
|
parentCtx.selectObservers[key].push(callback)
|
|
286
|
+
|
|
287
|
+
// Return an unsub that removes this callback, and register it in
|
|
288
|
+
// activeRenderCleanups so parent bindAs re-renders clean it up.
|
|
289
|
+
let active = true
|
|
290
|
+
const unsub = () => {
|
|
291
|
+
if (!active) return
|
|
292
|
+
active = false
|
|
293
|
+
const arr = parentCtx.selectObservers[key]
|
|
294
|
+
if (arr) {
|
|
295
|
+
const idx = arr.indexOf(callback)
|
|
296
|
+
if (idx !== -1) arr.splice(idx, 1)
|
|
297
|
+
if (arr.length === 0) delete parentCtx.selectObservers[key]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (activeRenderCleanups !== null) {
|
|
301
|
+
activeRenderCleanups.push(unsub)
|
|
302
|
+
}
|
|
303
|
+
return unsub
|
|
285
304
|
}
|
|
286
305
|
|
|
287
306
|
function doBindSelectAttr (attribute) {
|
|
@@ -335,10 +354,10 @@ function doSelect (key) {
|
|
|
335
354
|
const currentSelected = ctx.selected
|
|
336
355
|
ctx.selected = key
|
|
337
356
|
if (ctx.selectObservers[currentSelected] !== undefined) {
|
|
338
|
-
for (const obs of ctx.selectObservers[currentSelected]) obs(ctx.selected)
|
|
357
|
+
for (const obs of ctx.selectObservers[currentSelected].slice()) obs(ctx.selected)
|
|
339
358
|
}
|
|
340
359
|
if (ctx.selectObservers[ctx.selected] !== undefined) {
|
|
341
|
-
for (const obs of ctx.selectObservers[ctx.selected]) obs(ctx.selected)
|
|
360
|
+
for (const obs of ctx.selectObservers[ctx.selected].slice()) obs(ctx.selected)
|
|
342
361
|
}
|
|
343
362
|
}
|
|
344
363
|
|
|
@@ -410,14 +429,34 @@ function doBindChildren (parent, element) {
|
|
|
410
429
|
}
|
|
411
430
|
if (typeof ctx.mapKey !== 'function') {
|
|
412
431
|
console.warn('Using value index as key, may not work correctly when moving items...')
|
|
413
|
-
ctx.mapKey = (
|
|
432
|
+
ctx.mapKey = (_, i) => i
|
|
414
433
|
}
|
|
415
434
|
|
|
416
435
|
if (!Array.isArray(ctx.currentValue)) {
|
|
417
436
|
throw new Error('You can only use bindChildren with a state that contains an array. try myState([mystate]) before calling this function.')
|
|
418
437
|
}
|
|
419
438
|
|
|
420
|
-
|
|
439
|
+
const bindContext = { element, parent }
|
|
440
|
+
ctx.bindContexts.push(bindContext)
|
|
441
|
+
|
|
442
|
+
// If inside a parent bindAs render, register a cleanup that tears down all
|
|
443
|
+
// per-item subscriptions when the parent re-renders or is itself torn down.
|
|
444
|
+
// Without this, nested bindAttr/bindStyle/bindAs subscriptions created for
|
|
445
|
+
// each array item would be orphaned when the parent replaces this tree.
|
|
446
|
+
if (activeRenderCleanups !== null) {
|
|
447
|
+
activeRenderCleanups.push(() => {
|
|
448
|
+
if (bindContext.elementCleanups) {
|
|
449
|
+
for (const key in bindContext.elementCleanups) {
|
|
450
|
+
for (const unsub of bindContext.elementCleanups[key]) unsub()
|
|
451
|
+
}
|
|
452
|
+
bindContext.elementCleanups = {}
|
|
453
|
+
}
|
|
454
|
+
// Remove this bindContext so reconcile doesn't operate on stale state
|
|
455
|
+
const idx = ctx.bindContexts.indexOf(bindContext)
|
|
456
|
+
if (idx !== -1) ctx.bindContexts.splice(idx, 1)
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
|
|
421
460
|
this.subscribe((_, oldState) => {
|
|
422
461
|
if (!Array.isArray(ctx.currentValue)) {
|
|
423
462
|
console.warn('A state used with bindChildren was updated to a non array value. This will be converted to an array of 1 and the state will be updated.')
|
|
@@ -460,9 +499,10 @@ const doBind = function (ctx, element, handleReplace) {
|
|
|
460
499
|
// If inside a parent bindAs render, register a cascading cleanup that tears
|
|
461
500
|
// down both the driving subscription and all subscriptions created inside
|
|
462
501
|
// this bindAs's render, so they don't outlive the parent's re-render.
|
|
463
|
-
if (prevRenderCleanups !== null && elCtx.drivingUnsub) {
|
|
502
|
+
if (prevRenderCleanups !== null && (elCtx.drivingUnsub || elCtx.selectUnsub)) {
|
|
464
503
|
prevRenderCleanups.push(() => {
|
|
465
|
-
elCtx.drivingUnsub()
|
|
504
|
+
if (elCtx.drivingUnsub) elCtx.drivingUnsub()
|
|
505
|
+
if (elCtx.selectUnsub) elCtx.selectUnsub()
|
|
466
506
|
runCleanups(elCtx)
|
|
467
507
|
})
|
|
468
508
|
}
|
|
@@ -517,7 +557,9 @@ const updateReplacer = (ctx, element, elCtx) => (_, oldValue) => {
|
|
|
517
557
|
function doBindSelect (element) {
|
|
518
558
|
element = element ?? this
|
|
519
559
|
const ctx = this._ctx
|
|
520
|
-
return doBind(ctx, element, (elCtx) =>
|
|
560
|
+
return doBind(ctx, element, (elCtx) => {
|
|
561
|
+
elCtx.selectUnsub = subscribeSelect(ctx, updateReplacer(ctx, element, elCtx))
|
|
562
|
+
})
|
|
521
563
|
}
|
|
522
564
|
|
|
523
565
|
function doBindAs (element) {
|
|
@@ -541,13 +583,13 @@ function reconcile (ctx, oldState) {
|
|
|
541
583
|
}
|
|
542
584
|
}
|
|
543
585
|
|
|
544
|
-
function keyMapper (mapKey, value) {
|
|
586
|
+
function keyMapper (mapKey, value, i) {
|
|
545
587
|
if (typeof value !== 'object') {
|
|
546
588
|
return value
|
|
547
589
|
} else if (typeof mapKey !== 'function') {
|
|
548
590
|
return 0
|
|
549
591
|
} else {
|
|
550
|
-
return mapKey(value)
|
|
592
|
+
return mapKey(value, i)
|
|
551
593
|
}
|
|
552
594
|
}
|
|
553
595
|
|
|
@@ -585,7 +627,7 @@ function arrangeElements (ctx, bindContext, oldState) {
|
|
|
585
627
|
for (let i = 0; i < ctx.currentValue.length; i++) {
|
|
586
628
|
const rawValue = ctx.currentValue[i]
|
|
587
629
|
// Use the raw value to get the key
|
|
588
|
-
const key = keyMapper(ctx.mapKey, rawValue)
|
|
630
|
+
const key = keyMapper(ctx.mapKey, rawValue, i)
|
|
589
631
|
|
|
590
632
|
if (keys[key]) {
|
|
591
633
|
if (oldState) ctx.state(oldState)
|
|
@@ -610,7 +652,7 @@ function arrangeElements (ctx, bindContext, oldState) {
|
|
|
610
652
|
if (!Array.isArray(currentArr)) return
|
|
611
653
|
|
|
612
654
|
// find the element dynamically because sorting/filtering might have shifted the index
|
|
613
|
-
const idx = currentArr.findIndex(item => keyMapper(ctx.mapKey, item) === key)
|
|
655
|
+
const idx = currentArr.findIndex((item, i) => keyMapper(ctx.mapKey, item, i) === key)
|
|
614
656
|
|
|
615
657
|
if (idx > -1 && currentArr[idx] !== newItem) {
|
|
616
658
|
currentArr[idx] = newItem
|
|
@@ -671,9 +713,7 @@ function arrangeElements (ctx, bindContext, oldState) {
|
|
|
671
713
|
delete bindContext.boundElementByKey[keyToDelete]
|
|
672
714
|
delete bindContext.elementStates[keyToDelete] // Cleanup element state
|
|
673
715
|
|
|
674
|
-
|
|
675
|
-
delete ctx.selectObservers[keyToDelete]
|
|
676
|
-
}
|
|
716
|
+
delete ctx.selectObservers[keyToDelete]
|
|
677
717
|
prev.previousSibling.replaceWith(current)
|
|
678
718
|
} else if (isNew) {
|
|
679
719
|
// insertAdjacentElement is faster, but some nodes don't have it (lookin' at you text)
|