diff --git a/.gitignore b/.gitignore index 754bffd..e66f9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ theme.js theme.min.js config.json assets/css/pre-build +assets/js/libs diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0a00eac --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +app.js +theme.min.css +theme.min.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d38c088 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "useTabs": true, + "tabWidth": 4, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 100, + "semi": true, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/Gruntfile.js b/Gruntfile.js index 1b37678..a236099 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,5 @@ const findUsedJsFiles = (grunt) => { - const file = grunt.file.read('snippets/js.liquid', 'utf8'); + const file = grunt.file.read('snippets/js-modules.liquid', 'utf8'); const regex = /'([0-9a-zA-Z\-_\.]*.js)' \| asset_url/gm; let jsFiles = []; @@ -31,7 +31,6 @@ const findUsedJsLibs = (grunt) => { }; module.exports = function (grunt) { - let shopifyConfig = grunt.file.readJSON('config.json', 'utf8'); grunt.initConfig({ @@ -41,8 +40,8 @@ module.exports = function (grunt) { pkg: grunt.file.readJSON('package.json'), install: { options: { - env: ['default', 'staging', 'production'] - } + env: ['default', 'staging', 'production'], + }, }, shopify: { options: { @@ -58,15 +57,15 @@ module.exports = function (grunt) { 'locales/*', 'sections/*', 'snippets/*', - 'templates/*' - ] - } - } + 'templates/*', + ], + }, + }, }); const tasks = grunt.file.expand({ filter: 'isFile', cwd: 'tasks' }, ['*']); - tasks.forEach(task => { + tasks.forEach((task) => { require(`./tasks/${task}`)(grunt); }); @@ -79,22 +78,24 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-csso'); grunt.loadNpmTasks('grunt-babel'); - grunt.loadNpmTasks('grunt-purgecss'); grunt.loadNpmTasks('grunt-csso'); + grunt.loadNpmTasks('grunt-env'); grunt.loadNpmTasks('grunt-complexity'); + grunt.loadNpmTasks('grunt-prettier'); - grunt.registerTask('dev-js', ['complexity', 'jshint']); + grunt.registerTask('dev-js', ['complexity', 'prettier', 'jshint']); grunt.registerTask('dev', ['dev-js']); grunt.registerTask('js', ['dev-js', 'clean:js', 'libs', 'concat:js', 'babel', 'headers:js']); - grunt.registerTask('css', ['clean:css','postcss', 'concat:css', 'purgecss', 'csso', 'headers:css']); - grunt.registerTask('build', ['css', 'js']); + grunt.registerTask('css', ['clean:css', 'postcss', 'csso', 'headers:css']); + grunt.registerTask('build', ['env:build', 'css', 'js']); grunt.registerTask('deploy', ['build', 'shopify:deploy']); grunt.registerTask('deploy:staging', ['build', 'shopify:deploy:staging']); grunt.registerTask('deploy:prod', ['build', 'shopify:deploy:production']); grunt.registerTask('init', ['install', 'deploy']); - grunt.registerTask('default', ['watch']); + grunt.registerTask('default', ['env:dev', 'watch']); }; diff --git a/assets/css/base/fonts.css b/assets/css/base/fonts.css index 3cab780..dccfcdc 100644 --- a/assets/css/base/fonts.css +++ b/assets/css/base/fonts.css @@ -1,9 +1,7 @@ - @font-face { font-family: 'Font Name'; - src: local('?'), - url("/assets/fonts/file-name.woff2") format('woff2'), - url("/assets/fonts/file-name.woff") format('woff'); + src: local('?'), url('/assets/fonts/file-name.woff2') format('woff2'), + url('/assets/fonts/file-name.woff') format('woff'); font-weight: 400; font-style: normal; font-display: fallback; diff --git a/assets/css/base/site.css b/assets/css/base/site.css index 0f1b13d..9354161 100644 --- a/assets/css/base/site.css +++ b/assets/css/base/site.css @@ -1,33 +1,43 @@ ::selection { - @apply bg-main-reverse; - @apply text-main-reverse; + @apply bg-black; + @apply text-white; } ::-moz-selection { - @apply bg-main-reverse; - @apply text-main-reverse; + @apply bg-black; + @apply text-white; +} + +@media screen and (prefers-reduced-motion: reduce), (update: slow) { + *, + *::before, + *::after { + animation-duration: 0.001ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.001ms !important; + scroll-behavior: auto !important; + } } html { --unit-root: 1; --security-root: 60; --fluid-breakpoint-root: 1560; - --perimeter-root: calc( - var(--fluid-breakpoint-root) - var(--security-root) * 2 - ); - --unit-in-vw: calc( - var(--unit-root) * 100 / var(--fluid-breakpoint-root) * 1vw - ); + --perimeter-root: calc(var(--fluid-breakpoint-root) - var(--security-root) * 2); + --unit-in-vw: calc(var(--unit-root) * 100 / var(--fluid-breakpoint-root) * 1vw); --unit-in-px: calc(var(--unit-root) * 1px); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); + --ease-out-quad: cubic-bezier(0.5, 1, 0.89, 1); font-size: calc(var(--unit-in-vw) * 3); - scroll-behavior: smooth; + + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: transparent; } body { - @apply text-current; - @apply font-sans; + @apply text-40; + @apply font-base; } /* Tablet */ diff --git a/assets/css/main.css b/assets/css/main.css index e902bef..a981c7b 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -8,6 +8,3 @@ /* Utils */ @import 'tailwind/utilities.css'; - -/* Admin */ -@import 'lib/admin.css'; diff --git a/assets/js/core/app.js b/assets/js/core/app.js index bd44b44..180a942 100644 --- a/assets/js/core/app.js +++ b/assets/js/core/app.js @@ -1,60 +1 @@ -/** - * @author Deux Huit Huit - */ -(function ($) { - - 'use strict'; - - var modules = []; - - var resolveFx = function (key, mod) { - var actions = mod(); - var match = null; - - if (!!actions) { - match = actions; - - if (!!match && typeof match === 'function') { - return match; - } - - var path = key.split('.'); - - path.forEach(function (p) { - if (!match || typeof match !== 'object') { - return false; - } - - match = match[p]; - }); - - if (typeof match !== 'function') { - match = null; - } - } - - return match; - }; - - window.App = { - register: function (mod) { - modules.push(mod); - }, - notify: function (key, data) { - $.each(modules, function (index, mod) { - var fx = resolveFx(key, mod); - - if (!!fx) { - fx(data); - } - }); - } - }; - - var init = function () { - App.notify('app.init'); - }; - - $(init); - -})(jQuery); +function _toConsumableArray(arr){return _arrayWithoutHoles(arr)||_iterableToArray(arr)||_unsupportedIterableToArray(arr)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(o,minLen){if(!o)return;if(typeof o==="string")return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);if(n==="Object"&&o.constructor)n=o.constructor.name;if(n==="Map"||n==="Set")return Array.from(o);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen)}function _iterableToArray(iter){if(typeof Symbol!=="undefined"&&Symbol.iterator in Object(iter))return Array.from(iter)}function _arrayWithoutHoles(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr)}function _arrayLikeToArray(arr,len){if(len==null||len>arr.length)len=arr.length;for(var i=0,arr2=new Array(len);isp){var sLen=_stack.length;for(var x=sp;x1&&arguments[1]!==undefined?arguments[1]:{};if(!url){url=window.location.origin+"/"}options=Object.assign({},defaultFetchOptions(),options);return window.fetch(url,options)};global.App=Object.assign({},global.App,{loader:{load:load,isLoading:function isLoading(){return _isLoading}}})})(window);(function(global,undefined){'use strict';var argsToObject=function argsToObject(arg){if(!!arg.args&&!Array.isArray(arg.args)){arg.args=[arg.args]}var a={args:arg.args||arguments,fx:arg.fx||"warn",me:arg.me||"App"},t1=_typeof(a.args[0]);if(t1==="string"||t1==="number"||t1==="boolean"){a.args[0]="["+a.me+"] "+a.args[0]}return a};var _logs=[];var log=function log(arg){if(!arg){return this}var a=argsToObject(arg);if(App.debug()){if(typeof console[a.fx]!=="function"){a.fx="log"}if(!!window.console[a.fx].apply){window.console[a.fx].apply(window.console,a.args)}else{a.args.forEach(function logArgs(arg){window.console[a.fx](arg)})}}_logs.push(a);return this};global.App=Object.assign({},global.App,{log:log,logs:function logs(){return _logs}})})(window);(function(global,undefined){'use strict';var getCurrentUrl=function getCurrentUrl(){return document.location.pathname};var mediatorIsLoadingPage=false;var currentRouteUrl=getCurrentUrl();var currentPage=null;var previousPage=null;var previousUrl="";var validateMediatorState=function validateMediatorState(){if(!!mediatorIsLoadingPage){App.log({args:"Mediator is busy waiting for a page load.",fx:"error"})}return!mediatorIsLoadingPage};var canEnterNextPage=function canEnterNextPage(nextPage){var result=true;if(!nextPage.canEnter()){App.log({fx:"error",args:["Cannot enter page %s.",nextPage.key()]});result=false}return result};var canLeaveCurrentPage=function canLeaveCurrentPage(){var result=false;if(!currentPage){App.log({args:"No current page set.",fx:"error"})}else if(!currentPage.canLeave()){App.log({args:["Cannot leave page %s.",currentPage.key()],fx:"error"})}else{result=true}return result};var resolvePageAction=function resolvePageAction(key,data){if(!!currentPage){return App.actions.resolve(currentPage.actions,key,data)}else{App.log({args:"Can not notify page: No current page set.",fx:"error"})}};var notifyAll=function notifyAll(key,data,cb){var actions=[];var pa=resolvePageAction(key,data);if(!!pa){actions.push(pa)}actions=actions.concat(App.modules.resolve(key,data));App.actions.execute(actions,key,data,cb);return this};var notifyPage=function notifyPage(key,data,cb){var pa=resolvePageAction(key,data);if(!!pa){App.actions.execute([pa],key,data,cb)}return this};var gotoPage=function gotoPage(obj,previousPoppedUrl){var pageData=arguments.length>2&&arguments[2]!==undefined?arguments[2]:{};var changeUrl=arguments.length>3&&arguments[3]!==undefined?arguments[3]:true;var nextPage;var route="";var safeParseData=function safeParseData(data){try{var parser=new window.DOMParser;var doc=parser.parseFromString(data,"text/html");return doc}catch(ex){App.log({args:[ex.message],fx:"error"});App.modules.notify("pages.failedtoparse",{data:data,route:route,nextPage:nextPage,currentPage:currentPage})}return null};var enterLeave=function enterLeave(){var leavingPage=currentPage;pageData.firstTime=false;if(!nextPage.isInited()){nextPage.init();nextPage.setInited();pageData.firstTime=true}App.modules.notify("page.leaving",{page:leavingPage});leavingPage.leave(function(){currentPage=null;previousPage=leavingPage;previousUrl=!!previousPoppedUrl?previousPoppedUrl:getCurrentUrl();leavingPage=null;App.modules.notify("page.leave",{page:previousPage})});App.modules.notify("page.entering",{page:nextPage,route:route});nextPage.enter(function(){currentPage=nextPage;App.modules.notify("page.enter",{page:nextPage,route:route});mediatorIsLoadingPage=false},pageData)};var loadSuccess=function loadSuccess(response){if(!!response.redirected){window.history.replaceState({data:{mediator:true,type:"pushState",redirected:true}},"",response.url);nextPage=App.pages.getPageForHref(response.url);route=response.url;var node=document.querySelector(nextPage.selector());if(!!node){node.style.opacity=0;node.style.display="none";return enterLeave()}}return response.text().then(function(data){var htmldata=safeParseData(data);var node=htmldata.querySelector(nextPage.selector());var elem=document.querySelector(App.root());if(!node){App.log({args:["Could not find \"%s\" in xhr data.",nextPage.selector()],fx:"error"});mediatorIsLoadingPage=false;App.modules.notify("pages.notfound",{data:data,url:obj,response:response,status:response.status})}else{node.style.opacity=0;node.style.display="none";elem.appendChild(node);App.modules.notify("pages.loaded",{elem:elem,data:data,html:htmldata,url:obj,page:nextPage,node:node,response:response,status:response.status});enterLeave()}})};if(validateMediatorState()&&canLeaveCurrentPage()){if(typeof obj==="string"){nextPage=App.pages.getPageForHref(obj);route=obj}else{App.log({fx:"error",args:"Url parameter must be of type string got "+_typeof(obj)});return}if(!nextPage){App.modules.notify("pages.routeNotFound",{page:currentPage,url:obj});App.log({args:["Route \"%s\" was not found.",obj],fx:"error"})}else{if(canEnterNextPage(nextPage)){if(nextPage.key()===currentPage.key()){App.modules.notify("pages.navigateToCurrent",{page:nextPage,route:route});App.log("Next page is the current one")}else{if(!!changeUrl){window.history.pushState({data:{mediator:true}},"",obj);pageData.type="pushState"}App.modules.notify("pages.loading",{page:nextPage});App.modules.notify("pages.requestBeginPageTransition",{currentPage:currentPage,nextPage:nextPage,route:route});if(!App.pages.loaded(obj)){mediatorIsLoadingPage=true;App.loader.load(obj).then(loadSuccess).catch(function(event){App.modules.notify("pages.loaderror",{event:event,url:obj})})}else{enterLeave();App.modules.notify("pages.loaded",{elem:document.querySelector(App.root()),url:obj,page:nextPage})}}}else{App.log({args:["Route \"%s\" is invalid.",obj],fx:"error"})}}}return this};var initPage=function initPage(page){if(!!currentPage){App.log({args:["Previous current page will be changed",{currentPage:currentPage,previousPage:previousPage,newCurrentPage:page}],fx:"warning"})}currentPage=page;previousPage=previousPage||page;App.modules.notify("page.entering",{page:currentPage,route:currentRouteUrl});currentPage.enter(function currentPageEnterCallback(){App.modules.notify("page.enter",{page:currentPage,route:currentRouteUrl})},true)};global.App=Object.assign({},global.App,{mediator:{getCurrentUrl:getCurrentUrl,getCurrentPage:function getCurrentPage(){return currentPage},setCurrentPage:function setCurrentPage(page){currentPage=page},getPreviousUrl:function getPreviousUrl(){return previousUrl},getPreviousPage:function getPreviousPage(){return previousPage},notify:notifyAll,notifyCurrentPage:notifyPage,goto:gotoPage,init:initPage}})})(window);(function(global,undefined){'use strict';var modules={};var createAbstractModule=function createAbstractModule(){return{actions:function actions(){},init:function init(){}}};var createModule=function createModule(module){return Object.freeze(Object.assign({},createAbstractModule(),module))};var exportModule=function exportModule(key,module,override){if(typeof key!=="string"){App.log({args:["`key` must be a string",key],fx:"error"})}else if(!!modules[key]&&!override){App.log({args:["Overwriting module key %s is not allowed",key],fx:"error"})}else{modules[key]=createModule(module)}return modules[key]};var resolveActions=function resolveActions(key,data){return Object.keys(modules).map(function resolveAction(k){return App.actions.resolve(modules[k].actions,key,data)}).filter(function(a){return!!a})};var notifyModules=function notifyModules(key,data,cb){var actions=resolveActions(key,data);App.actions.execute(actions,key,data,cb);return this};global.App=Object.assign({},global.App,{modules:{models:function models(){return modules},exports:exportModule,notify:notifyModules,resolve:resolveActions}})})(window);(function(global,undefined){'use strict';var pageModels={};var pageInstances={};var activeRoutes={};var createPageModel=function createPageModel(key,model,override){var factory=function factory(pageData){var modelRef;var _isInited=false;if(_typeof(model)==="object"){modelRef=model}else if(typeof model==="function"){modelRef=model.call(this,key,pageData,override);if(_typeof(modelRef)!=="object"){App.log({args:["The exported page model function must return an object, "+"`%s` given (%s)",_typeof(modelRef),modelRef],fx:"error"});return null}}else{App.log({args:["The exported page model must be an object or a function, "+"`%s` given (%s)",_typeof(model),model],fx:"error"});return null}var getSelector=function getSelector(){return"[data-page-url=\"shopify\"]"};var base={actions:function actions(){},init:function init(){},canEnter:function canEnter(){return true},canLeave:function canLeave(){return true},model:function model(){return key},enter:function enter(next,data){var p=document.querySelector(getSelector());p.style.opacity=1;p.style.display="block";if(!!data.firstTime||data.type==="pushState"){window.scrollTo({top:0,left:0,behavior:"auto"})}App.callback(next)},leave:function leave(next){var p=document.querySelector(getSelector());p.style.opacity=0;p.style.display="none";App.callback(next)}};var overwrites=Object.freeze({key:function key(){return pageData.key},selector:function selector(){return getSelector()},data:function data(){return pageData},isInited:function isInited(){return _isInited},setInited:function setInited(){_isInited=true}});return Object.freeze(Object.assign({},base,modelRef,overwrites))};activeRoutes[key]=[];return factory};var createPage=function createPage(pageData,keyModel,override){var pageModel=pageModels[keyModel];var pageInst;if(!pageModel){App.log({args:["Model `%s` not found",keyModel],fx:"error"})}else{if(!!pageInstances[pageData.key]&&!override){App.log({args:["Overwriting page key `%s` is not allowed",pageData.key],fx:"error"})}else{pageInst=pageModel(pageData);if(!!pageInst){pageInstances[pageData.key]=pageInst}return pageInst}}return false};var registerPageModel=function registerPageModel(key,pageModel,override){var keyType=_typeof(key);if(keyType!=="string"){App.log({args:["`key` must be a string, `%s` given (%s).",keyType,key],fx:"error"})}else if(!!pageModels[key]&&!override){App.log({args:["Overwriting page model key `%s` is not allowed",key],fx:"error"})}else{pageModels[key]=Object.freeze(pageModel);return pageModel}return false};var exportPage=function exportPage(key,model,override){var pageModel=createPageModel(key,model,override);return registerPageModel(key,pageModel,override)};var routeMatchStrategies={regexp:function regexp(testRoute,route,cb){if(testRoute.test(route)){return cb()}return true},string:function string(testRoute,route,cb){var regex;route=decodeURIComponent(route);route=route.split("#")[0];if(testRoute===route){return cb()}if(testRoute.indexOf("^")!==0){testRoute="^"+testRoute}if(testRoute.indexOf("^")!==testRoute.length-1){testRoute=testRoute+"$"}if(testRoute.indexOf("*")){testRoute=testRoute.replace(new RegExp("\\*","gi"),".*")}try{regex=new RegExp(testRoute)}catch(ex){App.log({args:["Error while creating RegExp %s.\n%s",testRoute,ex],fx:"error"})}if(!!regex&®ex.test(route)){return cb()}return true}};var matchRoute=function matchRoute(route,routes){var index=-1;var found=function found(i){index=i;return false};if(typeof route!=="string"){App.log({args:"`route` must be a string",fx:"error"});return index}if(!!~route.indexOf("?")){route=route.split("?")[0]}if(!!route&&!!routes){if(!Array.isArray(routes)){routes=Object.values(routes)}routes.every(function matchOneRoute(testRoute,i){var routeType=_typeof(testRoute);var routeStrategy=routeMatchStrategies[routeType];var cb=function cb(){return found(i)};if(typeof routeStrategy==="function"){return routeStrategy(testRoute,route,cb)}else if(testRoute===route){return cb()}return true})}return index};var addRoutes=function addRoutes(keyModel,routes){if(!pageModels[keyModel]){App.log({fx:"error",args:"Model \""+keyModel+"\" not found."});return false}if(!activeRoutes[keyModel]){activeRoutes[keyModel]=[]}if(keyModel==="default"){App.log({fx:"error",args:"You can't add routes to the default model"});return false}activeRoutes[keyModel]=_toConsumableArray(new Set(activeRoutes[keyModel].concat(routes)));return activeRoutes[keyModel]};var removeRoutes=function removeRoutes(keyModel,routes){if(!pageModels[keyModel]){App.log({fx:"error",args:"Model \""+keyModel+"\" not found."});return false}return false};var getPageForHref=function getPageForHref(href){if(!!pageInstances[href]){return pageInstances[href]}href=href.split("#")[0];var model=null;for(var m in activeRoutes){if(activeRoutes.hasOwnProperty(m)){var modelRoutes=activeRoutes[m];var match=!!~matchRoute(href,modelRoutes);if(!!match){model=m;break}}}if(!model){model="default"}return createPage({key:href},model,true)};var loaded=function loaded(url){return!!document.querySelector(App.root()).querySelector("[data-page-url=\"shopify\"]")};registerPageModel("default",createPageModel("default",{},true),{});global.App=Object.assign({},global.App,{pages:{instances:function instances(key){if(!!key){return pageInstances[key]}return pageInstances},models:function models(){return pageModels},getPageForHref:getPageForHref,page:function page(keyOrRoute){var result=pageInstances[keyOrRoute];if(!!!result){result=getPageForHref(keyOrRoute)}return result},create:createPage,exports:exportPage,loaded:loaded,routes:{active:function active(){return activeRoutes},match:matchRoute,add:addRoutes,remove:removeRoutes}}})})(window);(function(global,undefined){'use strict';var storage=function storage(_storage){return{get:function get(key){if(!key){return}key+="";return _storage[key]},set:function set(key,value){var result=false;if(!!key){key+="";try{_storage[key]=!value?"":value+"";result=true}catch(e){App.log({args:e.message,me:"Storage",fx:"error"});result=false}}return result},remove:function remove(key){var result=false;if(!!key){key+="";try{_storage.removeItem(key);result=true}catch(e){App.log({args:e.message,me:"Storage",fx:"error"});result=false}}return result},clear:function clear(regexp){var result=false;try{if(!regexp){_storage.clear()}else{var remove=[];for(var i=0;i<_storage.length;i++){var key=_storage.key(i);if(regexp.test(key)){remove.push(key)}}for(var _i=0;_i 1) { html = $('