Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

element-adapter

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

element-adapter - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

2

dist/element-adapter.esm.js

@@ -1,1 +0,1 @@

const e=e=>e.length>0?Array.from(new Set(e)):e,t=(e,t)=>r=>e*r[t],r=e=>()=>e,s=(e,t)=>{const r=typeof e,s=typeof t;if(r===s)return!0;throw new Error(`type mismatch: a(${e}) is ${r} and b(${t}) is ${s}`)};var n={">":e=>(t,r)=>{const n=e(t);return s(r,n)&&r>n},">=":e=>(t,r)=>{const n=e(t);return s(r,n)&&r>=n},"<":e=>(t,r)=>{const n=e(t);return s(r,n)&&r<n},"<=":e=>(t,r)=>{const n=e(t);return s(r,n)&&r<=n},"==":e=>(t,r)=>{const n=e(t);return s(r,n)&&r===e(t)}};const i={width:"w%",height:"h%"},o=Object.entries(i).reduce((e,[t,r])=>({...e,[r]:t}),{}),a=(e,t,r)=>{if(0===t.length)return;const s=document.createElement("b");s.style.position="absolute",e.appendChild(s);const n=t.reduce((e,t)=>({...e,[t]:r(s,t)}),{});return e.removeChild(s),n},c=(e,t)=>a(e,t,(e,t)=>(e.style.width="1"+t,e.getBoundingClientRect().width)),p=(e,t)=>a(e.parentNode,t,(e,t)=>{const r=o[t];e.style[r]="1%";const{[r]:s}=e.getBoundingClientRect();return s}),u=e=>["width","height"].includes(e),h=(e,t)=>e>t?"landscape":e<t?"portrait":"square",d=(e,t)=>0===e&&0===t?1:e/t,l=["width","height","aspect-ratio","orientation","children","characters"],m=["%","cap","ch","em","ex","ic","lh","rem","rlh","vb","vh","vi","vw","vmin","vmax","mm","Q","cm","in","pt","pc"],w=new RegExp(`^(\\d+(\\.\\d+)?)(${m.join("|")})$`),g=/^\d+(\.\d+)?(px)?$/,f=e=>{const s=e.trim().toLowerCase().split(/\s*,\s*/),o=[],a=[],c=[],p=[];for(const e of s){const s=e.split(/\s*&&\s*/),u=[];for(const e of s){const[s,o,h]=e.split(/\s+/),[,d,,l]=h.match(w)||[],m="%"===l?i[s]:l,f=m&&parseFloat(d);let b;if(p.push(s),m)("%"===l?c:a).push(m),b=t(f,m);else{const e=h.match(g)?parseFloat(h):h;b=r(e)}u.push({[s]:n[o](b)})}o.push(u)}return{query:o,units:a,percentUnits:c,watchedProperties:p}},b=({query:e,unitsMeasurements:t,props:r})=>e.some(e=>e.every(e=>{const[[s,n]]=Object.entries(e);return n(t,r[s])})),v=["button","submit","image","checkbox","radio","hidden","range","reset"],y=e=>"INPUT"===e.tagName&&!v.includes(e.getAttribute("type"))||"TEXTAREA"===e.tagName,E=e=>y(e)?e.value.trim().length:e.isContentEditable?e.textContent.trim().length:0,C=e=>e.isContentEditable,q=(e,t)=>e.find(e=>e===t)||(t.parentNode?q(e,t.parentNode):void 0),U=(e,t,r=!1)=>{e.observe(t,{childList:!0,characterData:r,subtree:r})},O=e=>{const t=["width","height","orientation","aspect-ratio"];return e.some(e=>t.includes(e))},P=e=>e.includes("characters"),$=(e,t)=>P(e)&&t.isContentEditable,j=e=>e.includes("children"),A=(e,t)=>!y(t)&&($(e,t)||j(e)),x=(e,t)=>{const r={};if(O(t)){const{clientWidth:t,clientHeight:s}=e,{paddingTop:n,paddingRight:i,paddingBottom:o,paddingLeft:a}=window.getComputedStyle(e),c=t-(parseInt(a,10)+parseInt(i,10)),p=s-(parseInt(n,10)+parseInt(o,10));Object.assign(r,{width:c,height:p,orientation:h(c,p),"aspect-ratio":d(c,p)})}return P(t)&&(e.isContentEditable||y(e))&&(r.characters=E(e)),!j(t)||e.isContentEditable||y(e)||(r.children=e.childElementCount),r},L=({elt:e,props:t,queries:r,units:s,percentUnits:n})=>{(({elt:e,props:t,queries:r,unitsMeasurements:s})=>{const n={...t,characters:t.characters||0,children:t.children||0};for(const[t,i]of Object.entries(r))e.classList.toggle(t,b({query:i,unitsMeasurements:s,props:n}));for(const[r,s]of Object.entries(t))e.style.setProperty("--ea-"+r,u(r)?s+"px":s)})({elt:e,props:t,queries:r,unitsMeasurements:{...c(e,s),...p(e,n)}})},Q={},R=`((width|height)\\s+(((>|<)=?)|==)\\s+\\d+(\\.\\d+)?(${m.join("|")}|px)|(characters|children)\\s+(((>|<)=?)|==)\\s+\\d+|aspect-ratio\\s+(((>|<)=?)|==)\\s+\\d+(\\.\\d+)?|orientation\\s+==\\s+(landscape|portrait|square))`,M=`${R}(\\s+&&\\s+${R})*`,N=new RegExp(`^\\s*${M}(\\s*,\\s*${M})*\\s*$`);export default function({target:t,queries:r={},...s}={}){(e=>{for(const t of Object.values(e))if(!t||!t.match(N))throw new Error(`invalid query "${t}"`)})(r),(({watchedProperties:e})=>{if(e){if(!Array.isArray(e))throw new Error("watchedProperties must be an array");if(e.length<1||!e.every(e=>l.includes(e)))throw new Error("watchedProperties must be an array with at least one of "+l.join(", "))}})(s);const n=(e=>{if(!e)throw new Error("target must be provided");const t="length"in Object(e)?Array.isArray(e)?e:Array.from(e):[e];if(t.length<1)throw new Error("at least one Element must be provided as target");if(t.some(e=>!(e instanceof window.Element)))throw new Error(`target must be an Element or a list of Elements. Actual:\n[${t.map(e=>String(e)).join(", ")}]`);return t})(t),{compiledQueries:i,units:o,percentUnits:a,watchedProperties:c}=(t=>{const r={},s=[],n=[],i=[];for(const[e,o]of Object.entries(t)){const t=f(o);r[e]=t.query,s.push(...t.units),n.push(...t.percentUnits),i.push(...t.watchedProperties)}return{compiledQueries:r,units:e(s),percentUnits:e(n),watchedProperties:e(i)}})(r);if(c.length<1&&!s.watchedProperties)throw new Error("at least one query or one watched properties must be provided");const{unobserve:p,applyStyle:u}=(e=>{const{ResizeObserver:t,MutationObserver:r}=window,s=new WeakMap,n={...e,propsCache:s},{elements:i,compiledQueries:o,units:a,percentUnits:c,watchedProperties:p}=e,u=O(p)&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:s})=>n=>{for(const{contentRect:{width:i,height:o},target:a}of n){const n={...e.get(a),width:i,height:o,orientation:h(i,o),"aspect-ratio":d(i,o)};e.set(a,n),window.requestAnimationFrame(()=>L({elt:a,props:n,queries:t,units:r,percentUnits:s}))}})(n),m=u&&new t(u),w=P(p)&&i.some(y)&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:s})=>({target:n})=>{const i=n.value.trim().length,o=e.get(n);if(i!==o.characters){const i={...o,characters:E(n)};e.set(n,i),L({elt:n,props:i,queries:t,units:r,percentUnits:s})}})(n),g=(j(p)&&!i.every(y)||P(p)&&i.some(C))&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:s,elements:n,watchedProperties:i})=>(o,a)=>{a.disconnect();for(const{type:a,target:c}of o)if(["childList","characterData"].includes(a)){const o=q(n,c);if(o){let n,a;const c={...e.get(o)};j(i)&&!o.isContentEditable&&(c.children=o.childElementCount,n=!0),$(i,o)&&(c.characters=E(o),a=!0),(n||a)&&(e.set(o,c),L({elt:o,props:c,queries:t,units:r,percentUnits:s}))}}for(const e of n)A(i,e)&&U(a,e,$(i,e))})(n),f=g&&new r(g);for(const e of i){const t=x(e,p);s.set(e,t),m&&m.observe(e),w&&y(e)&&e.addEventListener("input",w),f&&A(p,e)&&f&&U(f,e,$(p,e)),L({elt:e,props:t,queries:o,units:a,percentUnits:c})}return{unobserve:()=>{s.set(Q,!0),(({elements:e,mutationObserver:t,resizeObserver:r,inputListener:s,behaviourCssClasses:n})=>{t&&t.disconnect();for(const t of e)r&&r.unobserve(t),s&&y(t)&&t.removeEventListener("input",s),t.classList.remove(...n),l.forEach(e=>t.style.removeProperty("--ea-"+e))})({elements:i,mutationObserver:f,resizeObserver:m,inputListener:w,behaviourCssClasses:Object.keys(o)})},applyStyle:()=>{s.get(Q)||i.forEach(e=>L({elt:e,props:s.get(e),queries:o,units:a,percentUnits:c}))}}})({elements:n,compiledQueries:i,units:o,percentUnits:a,watchedProperties:e([...c,...s.watchedProperties||[]])});return{removeAdaptiveBehaviour:p,applyAdaptiveBehaviour:u}}
const e=e=>e.length>0?Array.from(new Set(e)):e,t=(e,t)=>r=>e*r[t],r=e=>()=>e,n=(e,t)=>{const r=typeof e,n=typeof t;if(r===n)return!0;throw new Error(`type mismatch: a(${e}) is ${r} and b(${t}) is ${n}`)};var s={">":e=>(t,r)=>{const s=e(t);return n(r,s)&&r>s},">=":e=>(t,r)=>{const s=e(t);return n(r,s)&&r>=s},"<":e=>(t,r)=>{const s=e(t);return n(r,s)&&r<s},"<=":e=>(t,r)=>{const s=e(t);return n(r,s)&&r<=s},"==":e=>(t,r)=>{const s=e(t);return n(r,s)&&r===e(t)}};const i={width:"w%",height:"h%"},o=Object.entries(i).reduce((e,[t,r])=>({...e,[r]:t}),{}),a=(e,t,r)=>{if(0===t.length)return;const n=document.createElement("b");n.style.position="absolute",e.appendChild(n);const s=t.reduce((e,t)=>({...e,[t]:r(n,t)}),{});return e.removeChild(n),s},c=(e,t)=>a(e,t,(e,t)=>(e.style.width="1"+t,e.getBoundingClientRect().width)),p=(e,t)=>a(e.parentNode,t,(e,t)=>{const r=o[t];e.style[r]="1%";const{[r]:n}=e.getBoundingClientRect();return n}),u=e=>["width","height"].includes(e),d=(e,t)=>e>t?"landscape":e<t?"portrait":"square",h=(e,t)=>0===e&&0===t?1:e/t,l=["width","height","aspect-ratio","orientation","children","characters"],m=["%","cap","ch","em","ex","ic","lh","rem","rlh","vb","vh","vi","vw","vmin","vmax","mm","Q","cm","in","pt","pc"],w=new RegExp(`^(\\d+(\\.\\d+)?)(${m.join("|")})$`),f=/^\d+(\.\d+)?(px)?$/,g=e=>{const n=e.trim().toLowerCase().split(/\s*,\s*/),o=[],a=[],c=[],p=[];for(const e of n){const n=e.split(/\s*&&\s*/),u=[];for(const e of n){const[n,o,d]=e.split(/\s+/),[,h,,l]=d.match(w)||[],m="%"===l?i[n]:l,g=m&&parseFloat(h);let v;if(p.push(n),m)("%"===l?c:a).push(m),v=t(g,m);else{const e=d.match(f)?parseFloat(d):d;v=r(e)}u.push({[n]:s[o](v)})}o.push(u)}return{query:o,units:a,percentUnits:c,watchedProperties:p}},v=({query:e,unitsMeasurements:t,props:r})=>e.some(e=>e.every(e=>{const[[n,s]]=Object.entries(e);return s(t,r[n])})),b=["button","submit","image","checkbox","radio","hidden","range","reset"],y=e=>"INPUT"===e.tagName&&!b.includes(e.getAttribute("type"))||"TEXTAREA"===e.tagName,E=e=>y(e)?e.value.trim().length:e.isContentEditable?e.textContent.trim().length:0,C=e=>e.isContentEditable,q=(e,t)=>e.find(e=>e===t)||(t.parentNode?q(e,t.parentNode):void 0),U=(e,t,r=!1)=>{e.observe(t,{childList:!0,characterData:r,subtree:r})},$=e=>{const t=["width","height","orientation","aspect-ratio"];return e.some(e=>t.includes(e))},A=e=>e.includes("characters"),P=(e,t)=>A(e)&&t.isContentEditable,O=e=>e.includes("children"),j=(e,t)=>!y(t)&&(P(e,t)||O(e)),x=(e,t)=>{const r={};if($(t)){const{clientWidth:t,clientHeight:n}=e,{paddingTop:s,paddingRight:i,paddingBottom:o,paddingLeft:a}=window.getComputedStyle(e),c=t-(parseInt(a,10)+parseInt(i,10)),p=n-(parseInt(s,10)+parseInt(o,10));Object.assign(r,{width:c,height:p,orientation:d(c,p),"aspect-ratio":h(c,p)})}return A(t)&&(e.isContentEditable||y(e))&&(r.characters=E(e)),!O(t)||e.isContentEditable||y(e)||(r.children=e.childElementCount),r},L=new WeakMap,M=({elt:e,props:t,queries:r,units:n,percentUnits:s})=>{(({elt:e,props:t,queries:r,unitsMeasurements:n})=>{const s={...t,characters:t.characters||0,children:t.children||0};for(const[t,o]of r.entries()){const r=v({query:o,unitsMeasurements:n,props:s});var i;"string"==typeof t?e.classList.toggle(t,r):r!==Boolean(null==(i=L.get(e))?void 0:i.get(o))&&(t(e,s),L.get(e)||L.set(e,new WeakMap),L.get(e).set(o,r))}for(const[r,n]of Object.entries(t))e.style.setProperty("--ea-"+r,u(r)?n+"px":n)})({elt:e,props:t,queries:r,unitsMeasurements:{...c(e,n),...p(e,s)}})},B={},Q=`((width|height)\\s+(((>|<)=?)|==)\\s+\\d+(\\.\\d+)?(${m.join("|")}|px)|(characters|children)\\s+(((>|<)=?)|==)\\s+\\d+|aspect-ratio\\s+(((>|<)=?)|==)\\s+\\d+(\\.\\d+)?|orientation\\s+==\\s+(landscape|portrait|square))`,R=`${Q}(\\s+&&\\s+${Q})*`,N=new RegExp(`^\\s*${R}(\\s*,\\s*${R})*\\s*$`),k=["string","function"];export default function({target:t,queries:r={},...n}={}){(e=>{for(const[t,r]of Object.entries(e)){if(!t||!t.match(N))throw new Error(`invalid query "${t}"`);if(!k.includes(typeof r))throw new Error(`invalid behaviour "${r}"`)}})(r),(({watchedProperties:e})=>{if(e){if(!Array.isArray(e))throw new Error("watchedProperties must be an array");if(e.length<1||!e.every(e=>l.includes(e)))throw new Error("watchedProperties must be an array with at least one of "+l.join(", "))}})(n);const s=(e=>{if(!e)throw new Error("target must be provided");const t="length"in Object(e)?Array.isArray(e)?e:Array.from(e):[e];if(t.length<1)throw new Error("at least one Element must be provided as target");if(t.some(e=>!(e instanceof window.Element)))throw new Error(`target must be an Element or a list of Elements. Actual:\n[${t.map(e=>String(e)).join(", ")}]`);return t})(t),{compiledQueries:i,units:o,percentUnits:a,watchedProperties:c}=(t=>{const r=new Map,n=[],s=[],i=[];for(const[e,o]of Object.entries(t)){const t=g(e);r.set(o,t.query),n.push(...t.units),s.push(...t.percentUnits),i.push(...t.watchedProperties)}return{compiledQueries:r,units:e(n),percentUnits:e(s),watchedProperties:e(i)}})(r);if(c.length<1&&!n.watchedProperties)throw new Error("at least one query or one watched properties must be provided");const{unobserve:p,applyAdaptiveBehaviour:u}=(e=>{const{ResizeObserver:t,MutationObserver:r}=window,n=new WeakMap,s={...e,propsCache:n},{elements:i,compiledQueries:o,units:a,percentUnits:c,watchedProperties:p}=e,u=$(p)&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:n})=>s=>{for(const{contentRect:{width:i,height:o},target:a}of s){const s={...e.get(a),width:i,height:o,orientation:d(i,o),"aspect-ratio":h(i,o)};e.set(a,s),window.requestAnimationFrame(()=>M({elt:a,props:s,queries:t,units:r,percentUnits:n}))}})(s),m=u&&new t(u),w=A(p)&&i.some(y)&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:n})=>({target:s})=>{const i=s.value.trim().length,o=e.get(s);if(i!==o.characters){const i={...o,characters:E(s)};e.set(s,i),M({elt:s,props:i,queries:t,units:r,percentUnits:n})}})(s),f=(O(p)&&!i.every(y)||A(p)&&i.some(C))&&(({propsCache:e,compiledQueries:t,units:r,percentUnits:n,elements:s,watchedProperties:i})=>(o,a)=>{a.disconnect();for(const{type:a,target:c}of o)if(["childList","characterData"].includes(a)){const o=q(s,c);if(o){let s,a;const c={...e.get(o)};O(i)&&!o.isContentEditable&&(c.children=o.childElementCount,s=!0),P(i,o)&&(c.characters=E(o),a=!0),(s||a)&&(e.set(o,c),M({elt:o,props:c,queries:t,units:r,percentUnits:n}))}}for(const e of s)j(i,e)&&U(a,e,P(i,e))})(s),g=f&&new r(f);for(const e of i){const t=x(e,p);n.set(e,t),m&&m.observe(e),w&&y(e)&&e.addEventListener("input",w),g&&j(p,e)&&g&&U(g,e,P(p,e)),M({elt:e,props:t,queries:o,units:a,percentUnits:c})}return{unobserve:()=>{n.set(B,!0),(({elements:e,mutationObserver:t,resizeObserver:r,inputListener:n,behaviours:s})=>{t&&t.disconnect();const i=s.filter(e=>"string"==typeof e);for(const t of e)r&&r.unobserve(t),n&&y(t)&&t.removeEventListener("input",n),t.classList.remove(...i),l.forEach(e=>t.style.removeProperty("--ea-"+e))})({elements:i,mutationObserver:g,resizeObserver:m,inputListener:w,behaviours:[...o.keys()]}),function(e){for(const t of e)L.delete(t)}(i)},applyAdaptiveBehaviour:()=>{n.get(B)||i.forEach(e=>M({elt:e,props:n.get(e),queries:o,units:a,percentUnits:c}))}}})({elements:s,compiledQueries:i,units:o,percentUnits:a,watchedProperties:e([...c,...n.watchedProperties||[]])});return{removeAdaptiveBehaviour:p,applyAdaptiveBehaviour:u}}

@@ -10,8 +10,22 @@ import addAdaptiveBehaviour from '../dist/element-adapter.js'

queries: {
classA: `width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`,
classB: 'orientation == landscape',
classC: 'width > 75%',
classD: 'characters > 10',
classE: 'children >= 2 && children < 5',
classF: 'characters == 0'
[`width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`]:
'classA',
'orientation == landscape':
'classB',
'orientation == portrait' (element, props) {
console.debug(element, 'changed:\n', props)
},
'width > 75%':
'classC',
'characters > 10':
'classD',
'children >= 2 && children < 5':
'classE',
'characters == 0': 'classF'
}

@@ -18,0 +32,0 @@

{
"name": "element-adapter",
"version": "1.0.0",
"version": "2.0.0",
"description": "Provides utilities to render dom element adatptive (responsive) to their own properties – dimensions for instance. Can be used with shadow dom",

@@ -60,3 +60,6 @@ "keywords": [

"standard": "^14.3.3"
},
"volta": {
"node": "16.15.0"
}
}

@@ -33,2 +33,6 @@ # element-adapter

function logOrientationChangesToPortrait (element, props) {
console.debug(element, 'changed:\n', props)
}
addAdaptiveBehaviour({

@@ -38,7 +42,19 @@ target: document.querySelectorAll('.component'),

queries: {
classA: `width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`,
classB: 'orientation == landscape',
classC: 'width > 75%',
classD: 'characters == 0, characters > 10',
classE: 'children >= 2 && children < 5'
[`width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`]:
'classA',
'orientation == landscape':
'classB',
'orientation == portrait':
logOrientationChangesToPortrait,
'width > 75%':
'classC',
'characters == 0, characters > 10':
'classD',
'children >= 2 && children < 5':
'classE'
}

@@ -51,3 +67,4 @@ })

```javascript
// classA: `width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`
// [`width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`]:
// 'classA',

@@ -67,7 +84,18 @@ ADD 'classA' WHEN

```
**5th query:**
**3rd query:**
```javascript
// classE: 'children >= 2 && children < 5'
// 'orientation == portrait':
// (element, props) => {
// console.debug(element, 'changed:\n', props)
// },
EXECUTE FUNCTION logOrientationChangesToPortrait WHEN orientation === 'portrait'
```
**6th query:**
```javascript
// 'children >= 2 && children < 5':
// 'classE'
ADD 'classE' WHEN

@@ -114,7 +142,17 @@ children >= 2

queries: {
cssClassA: <query a>,
cssClassB: <query b>,
'query a': 'cssClassA',
'query b': 'cssClassB',
'query c': function onQueryMatch (element, props) {
// Do something when 'query c' matches element props
},
...
}
```
> ### Note
> Function behaviour parameters:
> - `element` is a reference to the DOM element which props the query is run against
> - `props` are the subset of props *({ key: value } pairs)* which the query is run against
#### Formal query syntax

@@ -203,3 +241,6 @@

> ### Note
> Function behaviours – *as opposed to classes behaviours* – don't get re-run when the element state hasn't changed
# Browsers compatibility
Tested on **Safari**, **Chrome** and **Firefox**

@@ -25,3 +25,3 @@ import { dedup } from './utils/array'

unobserve: removeAdaptiveBehaviour,
applyStyle: applyAdaptiveBehaviour
applyAdaptiveBehaviour
} = observe({

@@ -28,0 +28,0 @@ elements,

@@ -71,3 +71,5 @@ import {

const applyStyle = ({ elt, props, queries, unitsMeasurements }) => {
const functionBehaviourApplyCache = new WeakMap()
const applyAdaptiveBehaviour = ({ elt, props, queries, unitsMeasurements }) => {
const queryProps = {

@@ -79,12 +81,26 @@ ...props,

for (const [cls, query] of Object.entries(queries)) {
elt.classList.toggle(
cls,
for (const [behaviour, query] of queries.entries()) {
const isQueryMatched = runQuery({
query,
unitsMeasurements,
props: queryProps
})
runQuery({
query,
unitsMeasurements,
props: queryProps
})
)
if (typeof behaviour === 'string') {
elt.classList.toggle(behaviour, isQueryMatched)
} else {
const previousMatch = Boolean(functionBehaviourApplyCache.get(elt)?.get(query))
if (isQueryMatched !== previousMatch) {
if (isQueryMatched) {
behaviour(elt, queryProps)
}
if (!functionBehaviourApplyCache.get(elt)) {
functionBehaviourApplyCache.set(elt, new WeakMap())
}
functionBehaviourApplyCache.get(elt).set(query, isQueryMatched)
}
}
}

@@ -101,3 +117,3 @@

export const adapt = ({ elt, props, queries, units, percentUnits }) => {
applyStyle({
applyAdaptiveBehaviour({
elt,

@@ -113,1 +129,7 @@ props,

}
export function clearFunctionBehaviourApplyCache (elements) {
for (const elt of elements) {
functionBehaviourApplyCache.delete(elt)
}
}

@@ -22,3 +22,4 @@ import { WATCHABLE_PROPERTIES } from './constants'

computeInitialProps,
adapt
adapt,
clearFunctionBehaviourApplyCache
} from './adapters'

@@ -33,6 +34,8 @@

inputListener,
behaviourCssClasses
behaviours
}) => {
mutationObserver && mutationObserver.disconnect()
const behaviourCssClasses = behaviours.filter(behaviour => typeof behaviour === 'string')
for (const e of elements) {

@@ -114,7 +117,9 @@ resizeObserver && resizeObserver.unobserve(e)

inputListener,
behaviourCssClasses: Object.keys(compiledQueries)
behaviours: [...compiledQueries.keys()]
})
clearFunctionBehaviourApplyCache(elements)
},
applyStyle: () => {
applyAdaptiveBehaviour: () => {
if (!propsCache.get(INVALID)) {

@@ -121,0 +126,0 @@ elements.forEach(elt => adapt({

@@ -40,7 +40,13 @@ import { WATCHABLE_PROPERTIES, CSS_UNITS_BUT_PX } from './constants'

const allowedBehaviourTypes = ['string', 'function']
export const validateQueries = queries => {
for (const query of Object.values(queries)) {
for (const [query, behaviour] of Object.entries(queries)) {
if (!query || !query.match(QUERY_VALIDATOR_PATTERN)) {
throw new Error(`invalid query "${query}"`)
}
if (!allowedBehaviourTypes.includes(typeof behaviour)) {
throw new Error(`invalid behaviour "${behaviour}"`)
}
}

@@ -47,0 +53,0 @@ }

@@ -64,3 +64,3 @@ import { length, constant } from './calculators'

export const compileQueryList = queries => {
const compiledQueries = {}
const compiledQueries = new Map()
const units = []

@@ -70,6 +70,6 @@ const percentUnits = []

for (const [cssClass, query] of Object.entries(queries)) {
for (const [query, behaviour] of Object.entries(queries)) {
const compilationOut = compileQuery(query)
compiledQueries[cssClass] = compilationOut.query
compiledQueries.set(behaviour, compilationOut.query)
units.push(...compilationOut.units)

@@ -76,0 +76,0 @@ percentUnits.push(...compilationOut.percentUnits)

@@ -37,3 +37,3 @@ /* eslint-env jest */

unobserve: removeAdaptiveBehaviour,
applyStyle: applyAdaptiveBehaviour
applyAdaptiveBehaviour
}))

@@ -50,3 +50,3 @@

removeAdaptiveBehaviour: cleanUp,
applyAdaptiveBehaviour: applyStyle
applyAdaptiveBehaviour
} = addAdaptiveBehaviour({ target, queries, anyOptions })

@@ -65,3 +65,3 @@

expect(cleanUp).toBe(removeAdaptiveBehaviour)
expect(applyStyle).toBe(applyAdaptiveBehaviour)
expect(applyAdaptiveBehaviour).toBe(applyAdaptiveBehaviour)
})

@@ -68,0 +68,0 @@

/* eslint-env jest */
import {
computeInitialProps,
adapt
} from '../../src/lib/adapters'
import { runQuery } from '../../src/lib/query_processor'

@@ -37,8 +32,14 @@ import { measureNonPercentUnits, measurePercentUnits } from '../../src/utils/dimensions'

const queries = {
classA: function widthGreaterThanConstant100 () { return true },
classB: function charactersLesserThanConstant10 () { return true },
classC: function childrenGreaterThanConstant3 () { return false }
}
const notifySquareOrientation = jest.fn().mockName('notifySquareOrientation')
const notifyPortraitOrientation = jest.fn().mockName('notifyPortraitOrientation')
const orientationEqualPortrait = jest.fn(() => true).mockName('notifyPortraitOrientation')
const queries = new Map([
['classA', function widthGreaterThanConstant100 () { return true }],
['classB', function charactersLesserThanConstant10 () { return true }],
['classC', function childrenGreaterThanConstant3 () { return false }],
[notifySquareOrientation, function orientationEqualSquare () { return true }],
[notifyPortraitOrientation, orientationEqualPortrait]
])
runQuery.mockImplementation(({ query }) => query())

@@ -71,8 +72,20 @@

let computeInitialProps
let adapt
let clearFunctionBehaviourApplyCache
describe('lib/adapters', () => {
beforeEach(() => jest.isolateModules(() => ({
computeInitialProps,
adapt,
clearFunctionBehaviourApplyCache
} = require('../../src/lib/adapters'))))
afterEach(() => {
jest.clearAllMocks()
const behaviourCssClasses = [...queries.keys()].filter(behaviour => typeof behaviour === 'string')
elements.forEach(e => {
e.classList.remove(...Object.keys(queries))
e.classList.remove(...behaviourCssClasses)
WATCHABLE_PROPERTIES.forEach(prop => e.style.removeProperty(`--ea-${prop}`))

@@ -83,3 +96,3 @@ })

describe('#adapt', () => {
it('should apply behaviour classes according to queries results', () => {
it('should apply behaviours according to queries results', () => {
const props = {

@@ -93,6 +106,10 @@ width: 160,

const compiledQueries = Object.values(queries)
orientationEqualPortrait.mockReturnValueOnce(false)
expect(runQuery).toHaveBeenCalledTimes(compiledQueries.length)
adapt({ elt: div, props, queries, units, percentUnits })
const compiledQueries = [...queries.values()]
expect(runQuery).toHaveBeenCalledTimes(compiledQueries.length * 2)
compiledQueries.forEach(query => expect(runQuery).toHaveBeenCalledWith({

@@ -105,2 +122,5 @@ query,

expect(Array.from(div.classList)).toEqual(['classA', 'classB'])
expect(notifySquareOrientation).toHaveBeenCalledTimes(1)
expect(notifySquareOrientation).toHaveBeenCalledWith(div, props)
expect(notifyPortraitOrientation).toHaveBeenCalledTimes(1)
})

@@ -134,5 +154,5 @@

adapt({ elt: div, props, queries, units, percentUnits })
adapt({ elt: div, props, queries, units, percentUnits });
Object.values(queries).forEach(query => expect(runQuery).toHaveBeenCalledWith({
[...queries.values()].forEach(query => expect(runQuery).toHaveBeenCalledWith({
query,

@@ -150,2 +170,24 @@ unitsMeasurements,

describe('#clearFunctionBehaviourApplyCache', () => {
it('should actually clear cache', () => {
const props = {
width: 160,
characters: 8,
children: 1
}
const span = document.createElement('span')
adapt({ elt: div, props, queries, units, percentUnits })
adapt({ elt: span, props, queries, units, percentUnits })
clearFunctionBehaviourApplyCache([div])
adapt({ elt: div, props, queries, units, percentUnits })
adapt({ elt: span, props, queries, units, percentUnits })
expect(notifySquareOrientation).toHaveBeenCalledTimes(3)
})
})
describe('#computeInitialProps', () => {

@@ -152,0 +194,0 @@ it.each([

@@ -14,3 +14,4 @@ /* eslint-env jest */

computeInitialProps,
adapt
adapt,
clearFunctionBehaviourApplyCache
} from '../../src/lib/adapters'

@@ -30,3 +31,4 @@

computeInitialProps: jest.fn().mockName('computeInitialProps'),
adapt: jest.fn().mockName('adapt')
adapt: jest.fn().mockName('adapt'),
clearFunctionBehaviourApplyCache: jest.fn().mockName('clearFunctionBehaviourApplyCache')
}))

@@ -77,3 +79,3 @@

elements,
compiledQueries: { classA: '{ width: greaterThan(constant(300)) }' },
compiledQueries: new Map([[['classA'], ['{ width: greaterThan(constant(300)) }']]]),
units: ['em'],

@@ -137,3 +139,3 @@ percentUnits: ['w%']

elements,
compiledQueries: { classA: '{ characters: greaterThan(constant(300)) }' },
compiledQueries: new Map([[['classA'], ['{ characters: greaterThan(constant(300)) }']]]),
units: [],

@@ -235,3 +237,3 @@ percentUnits: []

elements,
compiledQueries: { classA: '{ children: greaterThan(constant(3)) }' },
compiledQueries: new Map([[['classA'], ['{ children: greaterThan(constant(3)) }']]]),
units: [],

@@ -412,3 +414,3 @@ percentUnits: []

elements,
compiledQueries: { classA: '{ width: greaterThan(constant(300)) }' },
compiledQueries: new Map([[['classA'], ['{ width: greaterThan(constant(300)) }']]]),
units: ['em'],

@@ -440,3 +442,3 @@ percentUnits: ['w%'],

elements,
compiledQueries: { classA: '{ width: greaterThan(constant(300)) }' },
compiledQueries: new Map([[['classA'], ['{ width: greaterThan(constant(300)) }']]]),
units: ['em'],

@@ -462,3 +464,3 @@ percentUnits: ['w%'],

const [div, span] = elements
const compiledQueries = { classA: '{ width: greaterThan(constant(300)) }' }
const compiledQueries = new Map([[['classA'], ['{ width: greaterThan(constant(300)) }']]])
const units = ['em']

@@ -512,7 +514,8 @@ const percentUnits = ['w%']

compiledQueries: {
classA: '{ width: greaterThan(constant(300)) }',
classB: '{ characters: greaterThan(constant(300)) }',
classC: '{ children: greaterThan(constant(3)) }'
},
compiledQueries: new Map([
['classA', '{ width: greaterThan(constant(300)) }'],
['classB', '{ characters: greaterThan(constant(300)) }'],
['classC', '{ children: greaterThan(constant(3)) }'],
[function notifySquareScreen () {}, 'equal(constant(square))']
]),

@@ -543,2 +546,3 @@ units: ['em'],

expect(textarea.removeEventListener).toHaveBeenCalledWith('input', inputListener)
expect(clearFunctionBehaviourApplyCache).toHaveBeenCalledWith(elements)
})

@@ -564,5 +568,7 @@

const behaviourCssClasses = [...params.compiledQueries.keys()].filter(key => typeof key === 'string')
elements.forEach(e => {
expect(e.classList.remove)
.toHaveBeenCalledWith(...Object.keys(params.compiledQueries))
.toHaveBeenCalledWith(...behaviourCssClasses)

@@ -585,7 +591,7 @@ expect(e.style.removeProperty).toHaveBeenCalledTimes(WATCHABLE_PROPERTIES.length)

const compiledQueries = {
classA: '{ width: greaterThan(constant(300)) }',
classB: '{ characters: greaterThan(constant(300)) }',
classC: '{ children: greaterThan(constant(3)) }'
}
const compiledQueries = new Map([
['classA', '{ width: greaterThan(constant(300)) }'],
['classB', '{ characters: greaterThan(constant(300)) }'],
['classC', '{ children: greaterThan(constant(3)) }']
])

@@ -618,7 +624,7 @@ const units = ['em']

const { applyStyle } = observeAll(params)
const { applyAdaptiveBehaviour } = observeAll(params)
adapt.mockClear()
applyStyle()
applyAdaptiveBehaviour()

@@ -638,3 +644,3 @@ expect(adapt).toHaveBeenCalledTimes(elements.length)

unobserve: cleanUp,
applyStyle
applyAdaptiveBehaviour
} = observeAll(params)

@@ -645,3 +651,3 @@

cleanUp()
applyStyle()
applyAdaptiveBehaviour()

@@ -648,0 +654,0 @@ expect(adapt).not.toHaveBeenCalled()

@@ -105,11 +105,27 @@ /* eslint-env jest */

expect(() => validateQueries({
classA: `width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`,
classB: ' orientation == landscape ',
classC: ' width > 75%',
classD: 'characters > 10',
classE: 'children >= 2 && children < 5 ',
classF: 'characters == 0',
classG: 'width >= 75em,height >= 80%',
classH: 'orientation == portrait',
classI: 'orientation == square'
[`width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`]:
'classA',
' orientation == landscape ':
'classB',
' width > 75%':
'classC',
'characters > 10':
'classD',
'children >= 2 && children < 5 ':
'classE',
'characters == 0':
'classF',
'width >= 75em,height >= 80%':
'classG',
'orientation == portrait':
'classH',
'orientation == square' () {}
})).not.toThrow()

@@ -147,7 +163,16 @@ })

expect(() => validateQueries({
classA: query,
classB: 'width > 75%'
[query]: 'classA',
'width > 75%': 'classB'
})).toThrow(`invalid query "${query}"`)
})
it('should reject query having invalid behaviour', () => {
const wrongBehaviour = ['wrong behaviour']
expect(() => validateQueries({
'orientation == square': wrongBehaviour,
'width > 75%': 'classB'
})).toThrow(`invalid behaviour "${wrongBehaviour}"`)
})
})
})

@@ -93,13 +93,29 @@ /* eslint-env jest */

it('should compile query list', () => {
function notifySquareScreen () {}
const compiled = compileQueryList({
classA: `width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`,
classB: 'orientation == landscape && height < 10.325em',
classC: 'width > 75%',
classD: 'characters > 10 && height <= 13.56%',
classE: 'children >= 2 && children < 5',
classF: 'characters == 0'
[`width >= 6.25em && height < 50%, aspect-ratio <= ${16 / 9}, width >= 680px`]:
'classA',
'orientation == landscape && height < 10.325em':
'classB',
'width > 75%':
'classC',
'characters > 10 && height <= 13.56%':
'classD',
'children >= 2 && children < 5':
'classE',
'characters == 0':
'classF',
'orientation == square':
notifySquareScreen
})
const compiledQueries = {
classA: [
const compiledQueries = new Map([
['classA', [
[

@@ -112,23 +128,24 @@ { width: 'greaterThanOrEqual(length(6.25, em))' },

[{ width: 'greaterThanOrEqual(constant(680))' }]
],
]],
classB: [[
['classB', [[
{ orientation: 'equal(constant(landscape))' },
{ height: 'lesserThan(length(10.325, em))' }
]],
]]],
classC: [[{ width: 'greaterThan(length(75, w%))' }]],
['classC', [[{ width: 'greaterThan(length(75, w%))' }]]],
classD: [[
['classD', [[
{ characters: 'greaterThan(constant(10))' },
{ height: 'lesserThanOrEqual(length(13.56, h%))' }
]],
]]],
classE: [[
['classE', [[
{ children: 'greaterThanOrEqual(constant(2))' },
{ children: 'lesserThan(constant(5))' }
]],
]]],
classF: [[{ characters: 'equal(constant(0))' }]]
}
['classF', [[{ characters: 'equal(constant(0))' }]]],
[notifySquareScreen, [[{ orientation: 'equal(constant(square))' }]]]
])

@@ -135,0 +152,0 @@ const units = ['em']

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc