transitions-refine 0.3.32 → 0.3.33
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/demo.html +70 -0
- package/package.json +1 -1
package/demo.html
CHANGED
|
@@ -2383,6 +2383,7 @@
|
|
|
2383
2383
|
for(const n of Object.keys(rec.prevVars||{})){try{const pv=rec.prevVars[n];if(pv)el.style.setProperty(n,pv);else el.style.removeProperty(n);}catch{}}
|
|
2384
2384
|
}
|
|
2385
2385
|
store.applied&&store.applied.clear();
|
|
2386
|
+
try{tlSyncNoneGuard(TL_LP_GUARD_PHASE_ID,null);}catch{}
|
|
2386
2387
|
}
|
|
2387
2388
|
|
|
2388
2389
|
// ── registry (per-lane overrides) ──
|
|
@@ -2666,6 +2667,66 @@
|
|
|
2666
2667
|
}catch{}
|
|
2667
2668
|
return out;
|
|
2668
2669
|
}
|
|
2670
|
+
// ── FLIP "jump-to-start" guard ──────────────────────────────────────────
|
|
2671
|
+
// Many transitions teleport to a start offset before animating, using a
|
|
2672
|
+
// state class that sets `transition: none` (the FLIP pattern), e.g.
|
|
2673
|
+
// `.t-text-swap.is-enter-start { transform: translateY(...); transition: none }`.
|
|
2674
|
+
// Our live-preview mirrors edited timings as an INLINE `transition` on the
|
|
2675
|
+
// element; inline wins (specificity 1,0,0,0) over that class rule, so the
|
|
2676
|
+
// instant teleport becomes an animation — the entering content then eases
|
|
2677
|
+
// in from the EXIT position instead of its start offset, flipping the
|
|
2678
|
+
// direction. Fix generally: find those page-authored `transition: none`
|
|
2679
|
+
// start-states and re-assert them with `!important` so the teleport stays
|
|
2680
|
+
// instant regardless of our inline transition. We only match STATE variants
|
|
2681
|
+
// (a selector whose subject compound carries a class the element is NOT
|
|
2682
|
+
// currently wearing) so we never freeze an element's BASE transition — the
|
|
2683
|
+
// base is what carries the lanes we add. @media blocks (e.g. reduced-motion)
|
|
2684
|
+
// are skipped so we don't promote their `transition: none` to always-on.
|
|
2685
|
+
const _TL_NONE_CACHE=new WeakMap();
|
|
2686
|
+
function tlCollectNoneSelectors(el){
|
|
2687
|
+
let cached=_TL_NONE_CACHE.get(el); if(cached)return cached;
|
|
2688
|
+
const out=new Set();
|
|
2689
|
+
try{
|
|
2690
|
+
const own=(typeof el.className==="string"?el.className.trim().split(/\s+/):Array.from(el.classList||[])).filter(Boolean);
|
|
2691
|
+
if(own.length){
|
|
2692
|
+
const ownSet=new Set(own);
|
|
2693
|
+
const esc=s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");
|
|
2694
|
+
const tokRes=own.map(c=>new RegExp("\\."+esc(c)+"(?![\\w-])"));
|
|
2695
|
+
for(const sheet of Array.from(document.styleSheets||[])){
|
|
2696
|
+
let rules;try{rules=sheet.cssRules;}catch{continue;}
|
|
2697
|
+
for(const rule of Array.from(rules||[])){
|
|
2698
|
+
if(rule.selectorText===undefined)continue; // skip @media / @supports etc.
|
|
2699
|
+
const tv=rule.style&&rule.style.getPropertyValue("transition");
|
|
2700
|
+
if(!tv||!/^\s*none\s*$/i.test(tv))continue;
|
|
2701
|
+
for(const part of rule.selectorText.split(",")){
|
|
2702
|
+
const s=part.trim(); if(!s)continue;
|
|
2703
|
+
// subject = rightmost compound that carries one of the element's tokens.
|
|
2704
|
+
const compounds=s.split(/\s*[>+~]\s*|\s+/).filter(Boolean);
|
|
2705
|
+
let subject=null;
|
|
2706
|
+
for(let i=compounds.length-1;i>=0;i--){ if(tokRes.some(re=>re.test(compounds[i]))){subject=compounds[i];break;} }
|
|
2707
|
+
if(!subject)continue;
|
|
2708
|
+
const subjClasses=(subject.match(/\.[-\w]+/g)||[]).map(c=>c.slice(1));
|
|
2709
|
+
// require a state class the element isn't currently wearing.
|
|
2710
|
+
if(subjClasses.some(c=>!ownSet.has(c)))out.add(s);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
}catch{}
|
|
2716
|
+
_TL_NONE_CACHE.set(el,out);
|
|
2717
|
+
return out;
|
|
2718
|
+
}
|
|
2719
|
+
function tlSyncNoneGuard(id,selectors){
|
|
2720
|
+
try{
|
|
2721
|
+
let st=document.getElementById(id);
|
|
2722
|
+
if(!selectors||!selectors.size){ if(st)st.remove(); return; }
|
|
2723
|
+
if(!st){ st=document.createElement("style"); st.id=id; (document.head||document.documentElement).appendChild(st); }
|
|
2724
|
+
const css=Array.from(selectors).map(s=>s+"{transition:none !important;}").join("\n");
|
|
2725
|
+
if(st.textContent!==css)st.textContent=css;
|
|
2726
|
+
}catch{}
|
|
2727
|
+
}
|
|
2728
|
+
const TL_LP_GUARD_FLAT_ID="tl-live-none-guard-flat";
|
|
2729
|
+
const TL_LP_GUARD_PHASE_ID="tl-live-none-guard-phase";
|
|
2669
2730
|
// Same, resolving the element from a CSS selector (used when only a lane's
|
|
2670
2731
|
// selector is known, e.g. a grouped phase member).
|
|
2671
2732
|
function laneValueHint(selector, property){
|
|
@@ -5147,12 +5208,17 @@
|
|
|
5147
5208
|
_TX_LIVE_BASE.delete(el);
|
|
5148
5209
|
applied.delete(el);
|
|
5149
5210
|
}
|
|
5211
|
+
// keep FLIP jump-to-start states instant under our inline transition
|
|
5212
|
+
const guard=new Set();
|
|
5213
|
+
for(const el of desired.keys()){ for(const s of tlCollectNoneSelectors(el)) guard.add(s); }
|
|
5214
|
+
tlSyncNoneGuard(TL_LP_GUARD_FLAT_ID,guard);
|
|
5150
5215
|
},[entries,registry]);
|
|
5151
5216
|
// restore everything on unmount
|
|
5152
5217
|
useEffect(()=>()=>{
|
|
5153
5218
|
const applied=liveAppliedRef.current;
|
|
5154
5219
|
for(const [el,rec] of applied){ try{el.style.transition=rec.prevInline;}catch{} for(const n of Object.keys(rec.vars||{})){try{const pv=(rec.prevVars||{})[n];if(pv)el.style.setProperty(n,pv);else el.style.removeProperty(n);}catch{}} _TX_LIVE_BASE.delete(el); }
|
|
5155
5220
|
applied.clear();
|
|
5221
|
+
tlSyncNoneGuard(TL_LP_GUARD_FLAT_ID,null);
|
|
5156
5222
|
},[]);
|
|
5157
5223
|
// ── live styling: grouped phases (state-gated) ──────────────────────────
|
|
5158
5224
|
// Open and close of one component share the SAME element(s), so a single
|
|
@@ -5277,6 +5343,10 @@
|
|
|
5277
5343
|
for(const n of Object.keys(rec.prevVars||{})){try{const pv=rec.prevVars[n];if(pv)el.style.setProperty(n,pv);else el.style.removeProperty(n);}catch{}}
|
|
5278
5344
|
store.applied.delete(el);
|
|
5279
5345
|
}
|
|
5346
|
+
// keep FLIP jump-to-start states instant under our inline transition
|
|
5347
|
+
const guard=new Set();
|
|
5348
|
+
for(const el of desiredCss.keys()){ for(const s of tlCollectNoneSelectors(el)) guard.add(s); }
|
|
5349
|
+
tlSyncNoneGuard(TL_LP_GUARD_PHASE_ID,guard);
|
|
5280
5350
|
};
|
|
5281
5351
|
// Coalesce via microtask (NOT rAF): the gate must update the moment the
|
|
5282
5352
|
// state class/attr flips — before the browser styles the next frame — so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transitions-refine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.33",
|
|
4
4
|
"description": "Live, agent-driven Refine panel for CSS/Motion transitions — injects a timeline + Refine UI and runs transitions.dev suggestions via your coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|