Browse Source

Implement svg optimisation

tripeur 4 years ago
parent
commit
230373b497
10 changed files with 455 additions and 39 deletions
  1. 6 2
      README.md
  2. 100 22
      minifyTemplate.js
  3. 152 0
      package-lock.json
  4. 3 1
      package.json
  5. 8 0
      test/css.test.js
  6. 30 0
      test/svg.test.js
  7. 0 9
      test/svg.test1.js
  8. 10 5
      test/template.test.js
  9. 146 0
      test/test1_input.css
  10. 0 0
      test/test1_output.css

+ 6 - 2
README.md

@@ -10,7 +10,6 @@ Webpack loader that minifies javascript files that contain template literals
 This loader takes `.js` files with html and css literals and minifies them with `html-minifier` and `clean-css`.
 
 ```javascript
-
 // converts this:
 export default
 html`<div>
@@ -24,7 +23,7 @@ export default html`<div>Awesome Template</div>`;
 ## Install
 
 ```bash
-$ npm install -D minify-lit-elt-template-literal-loader html-minifier
+$ npm install -D minify-lit-elt-template-literal-loader
 ```
 
 ## Use
@@ -47,6 +46,11 @@ config.module.rules = [
       collapseWhitespace: true
     }
   }
+  {
+    test: /\.tsx?$/,
+    use: 'ts-loader',
+    exclude: /node_modules/,
+  }
 ]
 ```
 

+ 100 - 22
minifyTemplate.js

@@ -1,11 +1,74 @@
-var U2 = require("uglify-js");
-let htmlMinify = require('html-minifier').minify;
-var CleanCSS = require('clean-css');
+const U2 = require("uglify-js");
+const htmlMinify = require('html-minifier').minify;
+const CleanCSS = require('clean-css');
+const svgo = require('svgo');
+const convert = require('xml-js');
 
 defaultOptions = {
   collapseWhitespace: true,
   minifyCSS:true,
+  minifySVG:true,
+  svgOptions:{
+    plugins: svgo.extendDefaultPlugins([
+      {
+        name: 'removeViewBox',
+        active: false
+      }
+    ]),
+  }
 }
+/**
+ * Extract element containing token to Cdata element
+ * @param {string} token
+ * @param {XMLElement} elt 
+ * @returns {XMLElement} tranform XML tree by stringifying element containing the token
+ */
+ const toCData =  function(elt,token){
+  let transform = elt.name == token;
+  if(elt.type=="text" && elt.text.includes(token)){
+    transform = true
+  }else if ("attributes" in elt ){
+    for (const k of Object.keys(elt.attributes)) {
+      if (k.includes(token)){
+        transform = true;
+      }else if(elt.attributes[k].includes(token)){
+        transform = true
+      }
+      if(transform){
+        break
+      }
+    }
+  }
+  if (transform){
+    const newElt = {
+      type:"cdata",
+      cdata:token + JSON.stringify(elt)
+    }
+    return newElt
+  }else{
+    if ('elements' in elt){
+      elt.elements = elt.elements.map(e=>toCData(e,token))
+    }
+    return elt
+  }
+}
+/**
+ * Recover extracted element containing token from Cdata element
+ * @param {string} token
+ * @param {XMLElement} elt 
+ * @returns {XMLElement} restored XML tree
+ */
+const fromCData = function(elt,token){
+  if (elt.type == "cdata" && elt.cdata.startsWith(token)){
+    return JSON.parse(elt.cdata.substr(token.length))	
+  }else{
+    if ('elements' in elt){
+      elt.elements = elt.elements.map(e=>fromCData(e,token))
+    }
+    return elt
+  }
+}
+
 module.exports = function (code, options){
   let mergedOptions = {...defaultOptions, ...options}
   const ast = U2.parse(code)
@@ -13,28 +76,43 @@ module.exports = function (code, options){
   if (mergedOptions.minifyCSS){
     cssOptions = typeof mergedOptions.minifyCSS == 'object' ? mergedOptions.minifyCSS:{}
   } 
-  // Accumulate lit-html tempaltes nodes in this array
-  const lit_html_templates = [];
+  let svgOptions = false;
+  if (mergedOptions.minifySVG){
+   svgOptions = mergedOptions.svgOptions ? mergedOptions.svgOptions : {};
+  }
   ast.walk(new U2.TreeWalker( (node)=>{
     if (node instanceof U2.AST_Template && node.tag && ['html','css','svg'].includes(node.tag.name)){
-      lit_html_templates.push(node);
-    }
-  }))
+      const templateContent = node.strings.map(s=>s.replace(/[\n|\r]/gm, ' '))
+      if(node.tag.name =="html"){
 
-  for (let i = lit_html_templates.length; --i >= 0 ;) {
-    var node = lit_html_templates[i];
-    const templateContent = node.strings.map(s=>s.replace(/[\n|\r]/gm, ' ')).join('${}')
-    let output
-    if(node.tag.name =="html"){
-      output = htmlMinify(templateContent, mergedOptions);
-    } else if(node.tag.name == "css" && cssOptions){
-      output = new CleanCSS(cssOptions).minify(templateContent).styles;
-    } else if(node.tag.name == "svg"){
-      // Todo implement svgo optimisation
-      output = htmlMinify(templateContent, mergedOptions);
-    } 
-    node.strings = output.split("${}");
-  }
+        const input = templateContent.join('${}');
+        const output = htmlMinify(input, mergedOptions)
+        node.strings = output.split("${}");
 
+      } else if(node.tag.name == "css" && cssOptions){
+        if (templateContent.length>1){
+          throw new Error("css literal cannot use template.\r\nMore info https://lit-element.polymer-project.org/api/modules/_lit_element_.html#css ")
+        }
+        const input = templateContent[0];
+        const output = new CleanCSS(cssOptions).minify(input).styles
+        node.strings = [output];
+      } else if(node.tag.name == "svg" && svgOptions){
+        const token = 'validTokenMlet'
+        const input = templateContent.join(token);
+        // Parse XML 
+        let obj = convert.xml2js(input)
+        // Transform elements containing template to CData elt 
+        obj = toCData(obj, token)
+        // Optimise svg (it does not affect Cdata elements)
+        let xml = convert.js2xml(obj);
+        xml = svgo.optimize(xml, svgOptions).data
+        // Revert CData elt to their original form
+        obj = convert.xml2js(xml)
+        obj = fromCData(obj,token)
+        // Reconstruct template 
+        node.strings = convert.js2xml(obj).split(token);
+      } 
+    }
+  }))
   return ast.print_to_string({ beautify: false });;
 }

+ 152 - 0
package-lock.json

@@ -663,6 +663,12 @@
         "@sinonjs/commons": "^1.7.0"
       }
     },
+    "@trysound/sax": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz",
+      "integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==",
+      "dev": true
+    },
     "@types/babel__core": {
       "version": "7.1.14",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
@@ -1090,6 +1096,12 @@
       "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
       "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
     },
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "dev": true
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1383,6 +1395,44 @@
         }
       }
     },
+    "css-select": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz",
+      "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0",
+        "css-what": "^4.0.0",
+        "domhandler": "^4.0.0",
+        "domutils": "^2.4.3",
+        "nth-check": "^2.0.0"
+      }
+    },
+    "css-tree": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz",
+      "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==",
+      "dev": true,
+      "requires": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      }
+    },
+    "css-what": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz",
+      "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==",
+      "dev": true
+    },
+    "csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dev": true,
+      "requires": {
+        "css-tree": "^1.1.2"
+      }
+    },
     "cssom": {
       "version": "0.4.4",
       "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@@ -1524,6 +1574,23 @@
       "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
       "dev": true
     },
+    "dom-serializer": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz",
+      "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.0.0",
+        "entities": "^2.0.0"
+      }
+    },
+    "domelementtype": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz",
+      "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==",
+      "dev": true
+    },
     "domexception": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
@@ -1541,6 +1608,26 @@
         }
       }
     },
+    "domhandler": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz",
+      "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==",
+      "dev": true,
+      "requires": {
+        "domelementtype": "^2.1.0"
+      }
+    },
+    "domutils": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.0.tgz",
+      "integrity": "sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg==",
+      "dev": true,
+      "requires": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.0.0"
+      }
+    },
     "ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -1583,6 +1670,12 @@
         "once": "^1.4.0"
       }
     },
+    "entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true
+    },
     "error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -3166,6 +3259,12 @@
         "object-visit": "^1.0.0"
       }
     },
+    "mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
+      "dev": true
+    },
     "merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -3373,6 +3472,15 @@
         "path-key": "^2.0.0"
       }
     },
+    "nth-check": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
+      "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0"
+      }
+    },
     "nwsapi": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@@ -4024,6 +4132,12 @@
         }
       }
     },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+      "dev": true
+    },
     "saxes": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
@@ -4334,6 +4448,12 @@
         "tweetnacl": "~0.14.0"
       }
     },
+    "stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "dev": true
+    },
     "stack-utils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz",
@@ -4445,6 +4565,29 @@
         "supports-color": "^7.0.0"
       }
     },
+    "svgo": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz",
+      "integrity": "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==",
+      "dev": true,
+      "requires": {
+        "@trysound/sax": "0.1.1",
+        "chalk": "^4.1.0",
+        "commander": "^7.1.0",
+        "css-select": "^3.1.2",
+        "css-tree": "^1.1.2",
+        "csso": "^4.2.0",
+        "stable": "^0.1.8"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+          "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+          "dev": true
+        }
+      }
+    },
     "symbol-tree": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -4847,6 +4990,15 @@
       "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==",
       "dev": true
     },
+    "xml-js": {
+      "version": "1.6.11",
+      "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+      "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+      "dev": true,
+      "requires": {
+        "sax": "^1.2.4"
+      }
+    },
     "xml-name-validator": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",

+ 3 - 1
package.json

@@ -18,7 +18,9 @@
     "clean-css": "^5.1.2",
     "html-minifier": "^4.0.0",
     "loader-utils": "^2.0.0",
-    "uglify-js": "^3.13.3"
+    "uglify-js": "^3.13.3",
+    "svgo": "^2.3.0",
+    "xml-js": "^1.6.11"
   },
   "devDependencies": {
     "jest": "^26.6.3"

+ 8 - 0
test/css.test.js

@@ -0,0 +1,8 @@
+const templateMinify = require("../minifyTemplate.js")
+const fs = require('fs');
+
+test('Test css string', () => {
+  input = 'css`' + fs.readFileSync("./test/test1_input.css",'utf-8') +'`';
+  ouput = 'css`' + fs.readFileSync("./test/test1_output.css",'utf-8') +'`;';
+  expect(templateMinify(input)).toBe(ouput);
+});

+ 30 - 0
test/svg.test.js

@@ -0,0 +1,30 @@
+const templateMinify = require("../minifyTemplate.js")
+
+test('Test svg string', () => {
+  str = `svg\`<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
+    <path d="M0 0h24v24H0z" fill="none"/>
+    <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
+  </svg>\`
+  `
+    expect(templateMinify(str)).toBe(`svg\`<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>\`;`);
+});
+
+test('Test svg string with template in attribute', () => {
+  expect(templateMinify('let echo = 0;svg`<svg xmlns="http://www.w3.org/2000/svg">\r\n<path d="M${echo} 0h24v24H0z"/>\r\n</svg>`',{collapseWhitespace: true}))
+                  .toBe('let echo=0;svg`<svg xmlns="http://www.w3.org/2000/svg"><path d="M${echo} 0h24v24H0z"/></svg>`;');
+});
+
+test('Test svg string with template as attribute', () => {
+  expect(templateMinify('let echo = 0;svg`<svg xmlns="http://www.w3.org/2000/svg">\<path ${echo}="2" d="M0 0h24v24H0z"/></svg>`',{collapseWhitespace: true}))
+                  .toBe('let echo=0;svg`<svg xmlns="http://www.w3.org/2000/svg"><path ${echo}="2" d="M0 0h24v24H0z"/></svg>`;');
+});
+
+test('Test svg string with template as text', () => {
+  expect(templateMinify('let echo = 0;svg`<svg xmlns="http://www.w3.org/2000/svg">\<path d="M0 0h24v24H0z">${echo}</path></svg>`',{collapseWhitespace: true}))
+                  .toBe('let echo=0;svg`<svg xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z">${echo}</path></svg>`;');
+});
+
+test('Test svg string with template as tag', () => {
+  expect(templateMinify('let echo = 0;svg`<svg xmlns="http://www.w3.org/2000/svg"><${echo}/></svg>`',{collapseWhitespace: true}))
+                  .toBe('let echo=0;svg`<svg xmlns="http://www.w3.org/2000/svg"><${echo}/></svg>`;');
+});

+ 0 - 9
test/svg.test1.js

@@ -1,9 +0,0 @@
-const templateMinify = require("../minifyTemplate.js")
-
-test('Test Empty string', () => {
-  str = `svg\`<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
-    <path d="M0 0h24v24H0z" fill="none"/><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
-  </svg>\`
-  `
-    expect(templateMinify(str)).toBe(`svg\`<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>\``);
-});

+ 10 - 5
test/template.test.js

@@ -1,11 +1,16 @@
 const templateMinify = require("../minifyTemplate.js")
 
-test('Test html string with tempalte', () => {
+test('Test html string with template', () => {
     expect(templateMinify("let echo = 1;\r html`\r\r\n <div> html test ${echo}\r</div> `",{collapseWhitespace: true}))
                     .toBe("let echo=1;html`<div>html test ${echo}</div>`;");
 });
 
-test('Test simple svg string', () => {
-    expect(templateMinify('let echo = 0;svg`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24">\r\n<path d="M${echo} 0h24v24H0z"/>\r\n</svg>`',{collapseWhitespace: true}))
-                    .toBe('let echo=0;svg`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24"><path d="M${echo} 0h24v24H0z"/></svg>`;');
-});
+test('Test html string with nested template', () => {
+    expect(templateMinify("let echo = 1;\r html`\r\r\n <div> html test ${echo ? html`<div>\r\nEcho\r\n</div>`:``}\r</div> `",{collapseWhitespace: true}))
+                    .toBe("let echo=1;html`<div>html test ${echo?html`<div>Echo</div>`:``}</div>`;");
+});
+
+test('Test html string with template in attribute', () => {
+    expect(templateMinify("let echo = 1;\r html`\r\r\n <div ${echo}> html test \r</div> `",{collapseWhitespace: true}))
+                    .toBe("let echo=1;html`<div ${echo}>html test</div>`;");
+});

+ 146 - 0
test/test1_input.css

@@ -0,0 +1,146 @@
+.ds-date-input-control {
+	width: 8rem;
+	margin-right: 4px;
+    position:relative;
+}
+.ds-date-input-title{
+    color:#505d74;
+    font-weight:bold;
+    padding-bottom:4px;
+}
+
+.ds-date-input[type="text"]{
+  all: initial;
+  color: #282e3a;
+  width: 100%;
+  border: none;
+  height: 2.5rem;
+  margin: 0;
+  outline: 0;
+  padding: 0.5rem;
+  font-size: 0.875rem;
+  -webkit-appearance: none;
+     -moz-appearance: none;
+          appearance: none;
+  box-sizing: border-box;
+  -webkit-transition: 0.1s ease-in-out;
+  transition: 0.1s ease-in-out;
+  font-family: Roboto, Helvetica Neue, Helvetica, Arial, sans-serif;
+  line-height: 1.25rem;
+  border-radius: 3px 3px 0 0;
+  background-color: #f7f9fa;
+  -webkit-transition-property: box-shadow, border;
+  transition-property: box-shadow, border;
+  border:none;
+  border-bottom:solid 2px grey;
+  background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzE4Mzc4MiI+PHBhdGggZD0iTTIwIDNoLTFWMWgtMnYySDdWMUg1djJINGMtMS4xIDAtMiAuOS0yIDJ2MTZjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY1YzAtMS4xLS45LTItMi0yem0wIDE4SDRWOGgxNnYxM3oiLz48L3N2Zz4=");
+  background-repeat: no-repeat;
+  background-position: right 8px top 45%;
+  background-size: 20px;
+}
+
+.ds-date-input[type="text"]:focus{
+    border-bottom:solid 2px #255fcc;
+}
+.ds-date-input[type="text"]:focus + .ds-datepicker-container,
+.ds-date-input[type="text"]:hover + .ds-datepicker-container{
+    display:block;
+}
+.ds-datepicker-container{
+  position: absolute;
+  width: 315px;
+  height: 360px;
+  display:none;
+  z-index:10;
+}
+.ds-datepicker-container.show{
+  display:block
+}
+.ds-datepicker-container.same-size{
+  width: 100%;
+  height:auto;
+  padding-top: 114%;
+}
+.ds-datepicker-container.right{
+    right:0px;
+}
+.ds-datepicker {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    background:white;
+	display:grid;
+	grid-template-columns: repeat( 7 , 1fr);
+    grid-template-rows: repeat( 8 , 1fr);
+    column-gap: 4px;
+    row-gap: 4px;
+    box-shadow: 0 2px 8px 0 rgba(0,0,0,0.14);
+    padding:8px;
+    z-index:10;
+}
+.ds-datepicker > div{
+    text-align:center;
+    color:#505d74;
+    border-radius:3px;
+    display:flex;
+    justify-content: center;
+    align-items: center;
+}
+.ds-datepicker-prev:before,
+.ds-datepicker-next:before{
+  content:"";
+  width:100%;height:100%;
+  background-repeat:no-repeat;
+  background-position: center;
+  background-size:35px;
+}
+.ds-datepicker-prev:before{
+   background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTE0LjcxIDYuNzFhLjk5Ni45OTYgMCAwMC0xLjQxIDBMOC43MSAxMS4zYS45OTYuOTk2IDAgMDAwIDEuNDFsNC41OSA0LjU5YS45OTYuOTk2IDAgMTAxLjQxLTEuNDFMMTAuODMgMTJsMy44OC0zLjg4Yy4zOS0uMzkuMzgtMS4wMyAwLTEuNDF6IiBmaWxsPSIjMTgzNzgyIi8+PC9zdmc+");  
+}
+.ds-datepicker-next:before{
+   background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTkuMjkgNi43MWEuOTk2Ljk5NiAwIDAwMCAxLjQxTDEzLjE3IDEybC0zLjg4IDMuODhhLjk5Ni45OTYgMCAxMDEuNDEgMS40MWw0LjU5LTQuNTlhLjk5Ni45OTYgMCAwMDAtMS40MUwxMC43IDYuN2MtLjM4LS4zOC0xLjAyLS4zOC0xLjQxLjAxeiIgZmlsbD0iIzE4Mzc4MiIvPjwvc3ZnPg==");
+}
+.ds-datepicker > div:not(.ds-datepicker-weekday):not(.ds-disable){
+    cursor:pointer;
+}
+.ds-datepicker > div:not(.ds-disable):not(.ds-datepicker-weekday):hover{
+    background: #f0f4f6; 
+    color:#505d74;
+}
+.ds-datepicker > div:not(.ds-disable):not(.ds-datepicker-weekday):focus{
+    border: solid 2px #255fcc;
+    border-radius:0px;
+}
+.ds-datepicker > div.ds-selected:not(.ds-disable):not(.ds-datepicker-weekday){
+    background: #063b9e;
+    color: #fff;
+    font-weight:bold;
+}
+.ds-datepicker-current {
+	grid-column: 2 / 7;
+    font-weight:bold;
+}
+div.ds-datepicker-weekday {
+	grid-column: 1 / 8;
+	display: grid;
+	grid-template-columns: repeat(7 , 1fr);
+    cursor:initial;
+}
+.ds-datepicker-weekday > div{
+    text-align:center;
+    color: #c1c7d3;
+}
+.ds-datepicker > .ds-today{
+    border: solid 2px #063b9e;
+    font-weight:bold;
+    color:#063b9e;
+}
+.ds-datepicker .ds-disable,
+.ds-datepicker .ds-out{
+  color: #c1c7d3;
+}
+.ds-datepicker > .ds-disable{
+    cursor:not-allowed;
+}

File diff suppressed because it is too large
+ 0 - 0
test/test1_output.css


Some files were not shown because too many files changed in this diff