diff --git a/exporters/js/exporter.json b/exporters/js/exporter.json index 2343e33d6b..cd0a96f8d2 100644 --- a/exporters/js/exporter.json +++ b/exporters/js/exporter.json @@ -5,7 +5,7 @@ "author": "Jan Kryšpín ", "organization": "LMC s.r.o.", "source_dir": "src", - "version": "1.0.0", + "version": "1.1.0", "usesBrands": true, "config": { "sources": "sources.json", diff --git a/exporters/js/generated/functions.js b/exporters/js/generated/functions.js index d0e1f55955..f75f04a6ce 100644 --- a/exporters/js/generated/functions.js +++ b/exporters/js/generated/functions.js @@ -1,33 +1,33 @@ -"use strict";function T(t){return t.replace(/\s/g,"-").replace(/\//g,"-").replace(/-\d\d-/g,"-").replace(/--+/g,"-").toLowerCase()}function E(t){return t.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1).toLowerCase()).join("")}function C(t){return t.split("-").map((n,r)=>r>0?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():n).join("")}function O(t){return t.slice(-1)==="s"?t.replace(/.$/,""):t}function H(t,n){let r="";const l=[];return Object.entries(t).forEach(([$,o])=>{const g=C($);l.push(g),o.length>0&&(r=`${r} -export const ${g} = { -${o.map(m=>` ${C(m)}: ${C($)}${E(m)},`).join(` +"use strict";function T(t){return t.replace(/\s/g,"-").replace(/\//g,"-").replace(/-\d\d-/g,"-").replace(/--+/g,"-").toLowerCase()}function L(t){return t.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1).toLowerCase()).join("")}function S(t){return t.split("-").map((n,i)=>i>0?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():n).join("")}function W(t){return t.slice(-1)==="s"?t.replace(/.$/,""):t}function w(t,n){let i="";const u=[];return Object.entries(t).forEach(([o,l])=>{const m=S(o);u.push(m),l.length>0&&(i=`${i} +export const ${m} = { +${l.map($=>` ${S($)}: ${S(o)}${L($)},`).join(` `)} }; -`)}),l.length>0&&(r=`${r} +`)}),u.length>0&&(i=`${i} export const ${n} = { - ${l.join(`, + ${u.join(`, `)}, }; -`),r}function d(t,n){let r=t.toString();return+t!=0&&(n==="Pixels"&&(r+="px"),n==="rem"&&(r+="rem")),r}function L(t){const n=t.match(/.{1,2}/g);let r=!0;return n&&n.forEach(l=>{r&&(r=/^(.)\1+$/.test(l))}),r?`${t.substring(0,1)}${t.substring(2,3)}${t.substring(4,5)}`:t}function W(t){return t.a<255?`#${t.hex}`:`#${L(t.hex.substring(0,6))}`}function M(t,n){const r=n.y-t.y,l=n.x-t.x;let o=Math.atan2(r,l)*180/Math.PI;return o+=90,(o<0?360+o:o)%360}function b(t,n){const r=T(t.origin?t.origin.name:t.name),l=T(n.origin?n.origin.name:n.name);return r.localeCompare(l)}function D(t,n){return+t.value.text-+n.value.text}function z(t,n,r,l){const $=r.trim().split(",");let o="",g="";if($.some(c=>((t.origin?t.origin.name:t.name).includes(c)&&(o=c),(n.origin?n.origin.name:n.name).includes(c)&&(g=c),!1)),l&&!o)return-1;let m=$.indexOf(o)-$.indexOf(g);return m===0&&(m=b(t,n)),m}function F(t){return t.name.match(/ \d$/)}function G(t,n,r=[],l=!1,$=!1,o="",g=""){let m=n.sort((e,a)=>{if(l){const p=e.name.match(/\d+$/),i=a.name.match(/\d+$/);if(p&&i)return parseInt(p[0],10)-parseInt(i[0],10)}return $?D(e,a):b(e,a)});o.length>0&&(m=n.sort((e,a)=>z(e,a,o,$)));const c=[],v={};m.forEach(e=>{if(g.length>0&&!e.name.startsWith(g))return;let a=T(e.name);e.origin&&!e.origin.name.toLowerCase().startsWith("gradients/gradient")&&(a=T(e.origin.name));const p=F(e);let i="";!p&&r.length>0&&r.forEach(f=>{Object.values(f.tokenIds).indexOf(e.id)>-1&&f.isRoot===!1&&(i=O(T(f.name)))});const x=a.split("-"),h=i===""?x[0]:i,S=i===""?a.replace(`${x[0]}-`,""):a.replace(`${i}-`,"");v[h]&&v[h].length>0?!p&&x[0]!==a?v[h].push(S):v[h]=[]:!p&&x[0]!==a?v[h]=[S]:v[h]=[];let u="";if(e.tokenType==="Color")u=W(e.value);else if(e.tokenType==="Radius")u=d(e.value.radius.measure,e.value.radius.unit);else if(e.tokenType==="GenericToken")e.propertyValues.unitless?u=e.value.text:u=d(e.value.text,"Pixels");else if(e.tokenType==="Shadow")u=`${d(e.value.x.measure,e.value.x.unit)} ${d(e.value.y.measure,e.value.y.unit)} ${d(e.value.radius.measure,e.value.radius.unit)} ${d(e.value.spread.measure,e.value.spread.unit)} ${W(e.value.color)}`;else if(e.tokenType==="Gradient"){let f="linear-gradient",P=`${Math.round(M(e.value.from,e.value.to)*100)/100}deg`;e.value.type==="Radial"&&(f="radial-gradient",P="circle at center"),u=`${f}(var(--angle, ${P}), ${e.value.stops.map(y=>`${W(y.color)} ${Math.round(y.position*10)/10*100}%`).join(", ")})`}else e.tokenType==="Border"?u=e.propertyValues.style??d(e.value.width.measure,e.value.width.unit):u=d(e.value.measure,e.value.unit);if(p){const f=T(e.name.replace(/ \d$/,"")),P=c.filter(j=>j.startsWith(`$${f}: `))[0],y=c.indexOf(P);y>-1&&(c[y]=c[y].replace(/= '(.*)';/g,`= '${u}, $1';`))}else c.push(`export const ${C(a)} = ${u==="0"?0:`'${u}'`};`)});const s=m.length===0?"":H(v,t);return`${c.join(` +`),i}function d(t,n){let i=t.toString();return+t!=0&&(n==="Pixels"&&(i+="px"),n==="rem"&&(i+="rem")),i}const j={SHORT:"short",LONG:"long"};function G(t,n=j.SHORT){const i=t.match(/.{1,2}/g);let u=n===j.SHORT;return i&&i.forEach(o=>{n===j.SHORT&&(u=/^(.)\1+$/.test(o))}),u?`${t.substring(0,1)}${t.substring(2,3)}${t.substring(4,5)}${t.substring(6,7)}`:n===j.LONG&&t.length===3?t.split("").map(o=>o+o).join(""):t}function E(t){return t.a<255?`#${t.hex}`:`#${G(t.hex.substring(0,6))}`}function M(t,n){const i=n.y-t.y,u=n.x-t.x;let l=Math.atan2(i,u)*180/Math.PI;return l+=90,(l<0?360+l:l)%360}function I(t,n){const i=T(t.origin?t.origin.name:t.name),u=T(n.origin?n.origin.name:n.name);return i.localeCompare(u)}function R(t,n){return+t.value.text-+n.value.text}function D(t,n,i,u){const o=i.trim().split(",");let l="",m="";if(o.some(p=>((t.origin?t.origin.name:t.name).includes(p)&&(l=p),(n.origin?n.origin.name:n.name).includes(p)&&(m=p),!1)),u&&!l)return-1;let $=o.indexOf(l)-o.indexOf(m);return $===0&&($=I(t,n)),$}function z(t){return t.name.match(/ \d$/)}function F(t,n,i=[],u=!1,o=!1,l="",m=""){let $=n.sort((e,a)=>{if(u){const f=e.name.match(/\d+$/),r=a.name.match(/\d+$/);if(f&&r)return parseInt(f[0],10)-parseInt(r[0],10)}return o?R(e,a):I(e,a)});l.length>0&&($=n.sort((e,a)=>D(e,a,l,o)));const p=[],v={};$.forEach(e=>{if(m.length>0&&!e.name.startsWith(m))return;let a=T(e.name);e.origin&&!e.origin.name.toLowerCase().startsWith("gradients/gradient")&&(a=T(e.origin.name));const f=z(e);let r="";!f&&i.length>0&&i.forEach(g=>{Object.values(g.tokenIds).indexOf(e.id)>-1&&g.isRoot===!1&&(r=W(T(g.name)))});const y=a.split("-"),h=r===""?y[0]:r,C=r===""?a.replace(`${y[0]}-`,""):a.replace(`${r}-`,"");v[h]&&v[h].length>0?!f&&y[0]!==a?v[h].push(C):v[h]=[]:!f&&y[0]!==a?v[h]=[C]:v[h]=[];let c="";if(e.tokenType==="Color")c=E(e.value);else if(e.tokenType==="Radius")c=d(e.value.radius.measure,e.value.radius.unit);else if(e.tokenType==="GenericToken")e.propertyValues.unitless?c=e.value.text:c=d(e.value.text,"Pixels");else if(e.tokenType==="Shadow")c=`${d(e.value.x.measure,e.value.x.unit)} ${d(e.value.y.measure,e.value.y.unit)} ${d(e.value.radius.measure,e.value.radius.unit)} ${d(e.value.spread.measure,e.value.spread.unit)} ${E(e.value.color)}`;else if(e.tokenType==="Gradient"){let g="linear-gradient",O=`${Math.round(M(e.value.from,e.value.to)*100)/100}deg`;e.value.type==="Radial"&&(g="radial-gradient",O="circle at center"),c=`${g}(var(--angle, ${O}), ${e.value.stops.map(x=>`${E(x.color)} ${Math.round(x.position*10)/10*100}%`).join(", ")})`}else e.tokenType==="Border"?c=e.propertyValues.style??d(e.value.width.measure,e.value.width.unit):c=d(e.value.measure,e.value.unit);if(f){const g=T(e.name.replace(/ \d$/,"")),O=p.filter(P=>P.startsWith(`$${g}: `))[0],x=p.indexOf(O);x>-1&&(p[x]=p[x].replace(/= '(.*)';/g,`= '${c}, $1';`))}else p.push(`export const ${S(a)} = ${c==="0"?0:`'${c}'`};`)});const s=$.length===0?"":w(v,t);return`${p.join(` `)} -${s}`}const V={400:300,600:400};function A(t,n,r,l=""){const $=t.sort(b),o=l.trim().split(","),g={};$.forEach(s=>{var w;const e=T(((w=s.origin)==null?void 0:w.name)||s.name);let a=e,p=o[0];o.forEach(I=>{a.includes(I)&&(p=I,a=a.replace(`-${I}`,""))});const i=d(Math.round(s.value.fontSize.measure/n*1e3)/1e3,"rem");let x="normal",h=+s.value.font.subfamily;s.value.font.family==="Ebony"&&(h=V[h]),e.includes("italic")&&(x="italic");const S=s.value.lineHeight&&Math.round(s.value.lineHeight.measure/100*1e3)/1e3,u=d(s.value.letterSpacing.measure,s.value.letterSpacing.unit),f=s.value.textDecoration.toLowerCase(),P=d(s.value.paragraphIndent.measure,s.value.paragraphIndent.unit),y=s.value.textCase==="Original"?"none":s.value.textCase.toLowerCase(),j={fontFamily:`'${s.value.font.family}'${r}`,fontSize:i,fontStyle:x,fontWeight:h,lineHeight:S,letterSpacing:u,textDecoration:f,paragraphIndent:P,textTransform:y};typeof g[a]<"u"?g[a][p]=j:g[a]={[p]:j}});const m=[],c=[];Object.entries(g).forEach(([s,e])=>{if(s.includes("-link"))return;c.push(`${C(s)}: ${C(s)},`);const a=[];o.forEach(p=>{const i=e[p];if(typeof i<"u"){const x=i.lineHeight?` - lineHeight: ${i.lineHeight},`:"",h=i.letterSpacing!=="0"?` - letterSpacing: ${i.letterSpacing},`:"",S=i.textDecoration!=="none"?` - textDecoration: ${i.textDecoration},`:"",u=i.paragraphIndent!=="0"?` - textIndent: ${i.paragraphIndent},`:"",f=i.textTransform!=="none"?` - textTransform: ${i.textTransform},`:"";a.push(`${p}: { - fontFamily: "${i.fontFamily}", - fontSize: '${i.fontSize}', - fontStyle: '${i.fontStyle}', - fontWeight: ${i.fontWeight},${x}${h}${S}${u}${f} - },`)}}),m.push(`export const ${C(s)} = { +${s}`}const N={400:300,600:400};function V(t,n,i,u=""){const o=t.sort(I),l=u.trim().split(","),m={};o.forEach(s=>{var b;const e=T(((b=s.origin)==null?void 0:b.name)||s.name);let a=e,f=l[0];l.forEach(H=>{a.includes(H)&&(f=H,a=a.replace(`-${H}`,""))});const r=d(Math.round(s.value.fontSize.measure/n*1e3)/1e3,"rem");let y="normal",h=+s.value.font.subfamily;s.value.font.family==="Ebony"&&(h=N[h]),e.includes("italic")&&(y="italic");const C=s.value.lineHeight&&Math.round(s.value.lineHeight.measure/100*1e3)/1e3,c=d(s.value.letterSpacing.measure,s.value.letterSpacing.unit),g=s.value.textDecoration.toLowerCase(),O=d(s.value.paragraphIndent.measure,s.value.paragraphIndent.unit),x=s.value.textCase==="Original"?"none":s.value.textCase.toLowerCase(),P={fontFamily:`'${s.value.font.family}'${i}`,fontSize:r,fontStyle:y,fontWeight:h,lineHeight:C,letterSpacing:c,textDecoration:g,paragraphIndent:O,textTransform:x};typeof m[a]<"u"?m[a][f]=P:m[a]={[f]:P}});const $=[],p=[];Object.entries(m).forEach(([s,e])=>{if(s.includes("-link"))return;p.push(`${S(s)}: ${S(s)},`);const a=[];l.forEach(f=>{const r=e[f];if(typeof r<"u"){const y=r.lineHeight?` + lineHeight: ${r.lineHeight},`:"",h=r.letterSpacing!=="0"?` + letterSpacing: ${r.letterSpacing},`:"",C=r.textDecoration!=="none"?` + textDecoration: ${r.textDecoration},`:"",c=r.paragraphIndent!=="0"?` + textIndent: ${r.paragraphIndent},`:"",g=r.textTransform!=="none"?` + textTransform: ${r.textTransform},`:"";a.push(`${f}: { + fontFamily: "${r.fontFamily}", + fontSize: '${r.fontSize}', + fontStyle: '${r.fontStyle}', + fontWeight: ${r.fontWeight},${y}${h}${C}${c}${g} + },`)}}),$.push(`export const ${S(s)} = { ${a.join(` `)} }; `)});const v=`export const styles = { - ${c.join(` + ${p.join(` `)} -};`;return`${m.join(` +};`;return`${$.join(` `)} ${v} -`}Pulsar.registerFunction("generateSimple",G);Pulsar.registerFunction("generateTypography",A); +`}Pulsar.registerFunction("generateSimple",F);Pulsar.registerFunction("generateTypography",V); diff --git a/exporters/js/src/js/formatters/__tests__/color.test.ts b/exporters/js/src/js/formatters/__tests__/color.test.ts index df541b755d..2293bee7bb 100644 --- a/exporters/js/src/js/formatters/__tests__/color.test.ts +++ b/exporters/js/src/js/formatters/__tests__/color.test.ts @@ -4,6 +4,7 @@ describe('formatColor', () => { it.each([ // name, expected [{ a: 125, hex: '123456' }, '#123456'], + [{ a: 563, hex: 'fff' }, '#fff'], ])('should format color', (color, expected) => { expect(formatColor(color)).toBe(expected); }); diff --git a/exporters/js/src/js/normalizers/__tests__/color.test.js b/exporters/js/src/js/normalizers/__tests__/color.test.js index 11f5a9ea22..9492610846 100644 --- a/exporters/js/src/js/normalizers/__tests__/color.test.js +++ b/exporters/js/src/js/normalizers/__tests__/color.test.js @@ -4,6 +4,9 @@ describe('normalizeColor', () => { it.each([ // name, expected ['123456', '123456'], + ['fff', 'fff'], + ['ffffff', 'fff'], + ['ffffff00', 'fff0'], ])('should normalize color', (color, expected) => { expect(normalizeColor(color)).toBe(expected); }); diff --git a/exporters/js/src/js/normalizers/color.ts b/exporters/js/src/js/normalizers/color.ts index 27b18e4b8d..380aa8e5d5 100644 --- a/exporters/js/src/js/normalizers/color.ts +++ b/exporters/js/src/js/normalizers/color.ts @@ -1,15 +1,31 @@ -export function normalizeColor(color: string): string { +const COLOR_HEX_LENGTH = { + SHORT: 'short', + LONG: 'long', +}; + +type ColorHexLength = (typeof COLOR_HEX_LENGTH)[keyof typeof COLOR_HEX_LENGTH]; + +export function normalizeColor(color: string, colorHexLength: ColorHexLength = COLOR_HEX_LENGTH.SHORT): string { const colorParts = color.match(/.{1,2}/g); - let shortHex = true; + let shortHex = colorHexLength === COLOR_HEX_LENGTH.SHORT; colorParts && colorParts.forEach((part) => { - if (shortHex) { + if (colorHexLength === COLOR_HEX_LENGTH.SHORT) { shortHex = /^(.)\1+$/.test(part); } }); if (shortHex) { - return `${color.substring(0, 1)}${color.substring(2, 3)}${color.substring(4, 5)}`; + return `${color.substring(0, 1)}${color.substring(2, 3)}${color.substring(4, 5)}${color.substring(6, 7)}`; + } + + if (colorHexLength === COLOR_HEX_LENGTH.LONG && color.length === 3) { + // Convert short hex to long hex + // @see: https://www.30secondsofcode.org/js/s/extend-hex/ + return color + .split('') + .map((x) => x + x) + .join(''); } return color;