learningtribes-studio-frontend 1.4.2-alpha.3 → 1.4.2-alpha.5

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.
@@ -1,2 +1,2 @@
1
- !function(r){function n(n){for(var t,f,i=n[0],l=n[1],p=n[2],a=0,c=[];a<i.length;a++)f=i[a],o[f]&&c.push(o[f][0]),o[f]=0;for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(r[t]=l[t]);for(s&&s(n);c.length;)c.shift()();return u.push.apply(u,p||[]),e()}function e(){for(var r,n=0;n<u.length;n++){for(var e=u[n],t=!0,i=1;i<e.length;i++){var l=e[i];0!==o[l]&&(t=!1)}t&&(u.splice(n--,1),r=f(f.s=e[0]))}return r}var t={},o={2:0},u=[];function f(n){if(t[n])return t[n].exports;var e=t[n]={i:n,l:!1,exports:{}};return r[n].call(e.exports,e,e.exports,f),e.l=!0,e.exports}f.m=r,f.c=t,f.d=function(r,n,e){f.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:e})},f.r=function(r){Object.defineProperty(r,"__esModule",{value:!0})},f.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return f.d(n,"a",n),n},f.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},f.p="";var i=window.studiofrontendJsonP=window.studiofrontendJsonP||[],l=i.push.bind(i);i.push=n,i=i.slice();for(var p=0;p<i.length;p++)n(i[p]);var s=l;u.push([436,0]),e()}([]);
1
+ !function(r){function n(n){for(var t,f,i=n[0],l=n[1],p=n[2],a=0,c=[];a<i.length;a++)f=i[a],o[f]&&c.push(o[f][0]),o[f]=0;for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(r[t]=l[t]);for(s&&s(n);c.length;)c.shift()();return u.push.apply(u,p||[]),e()}function e(){for(var r,n=0;n<u.length;n++){for(var e=u[n],t=!0,i=1;i<e.length;i++){var l=e[i];0!==o[l]&&(t=!1)}t&&(u.splice(n--,1),r=f(f.s=e[0]))}return r}var t={},o={2:0},u=[];function f(n){if(t[n])return t[n].exports;var e=t[n]={i:n,l:!1,exports:{}};return r[n].call(e.exports,e,e.exports,f),e.l=!0,e.exports}f.m=r,f.c=t,f.d=function(r,n,e){f.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:e})},f.r=function(r){Object.defineProperty(r,"__esModule",{value:!0})},f.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return f.d(n,"a",n),n},f.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},f.p="";var i=window.studiofrontendJsonP=window.studiofrontendJsonP||[],l=i.push.bind(i);i.push=n,i=i.slice();for(var p=0;p<i.length;p++)n(i[p]);var s=l;u.push([434,0]),e()}([]);
2
2
  //# sourceMappingURL=i18nMessages.min.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","r","value","n","__esModule","object","property","p","jsonpArray","window","oldJsonpFunction","bind","slice"],"mappings":"aACA,SAAAA,EAAAC,GAOA,IANA,IAKAC,EAAAC,EALAC,EAAAH,EAAA,GACAI,EAAAJ,EAAA,GACAK,EAAAL,EAAA,GAGAM,EAAA,EAAAC,KACQD,EAAAH,EAAAK,OAAoBF,IAC5BJ,EAAAC,EAAAG,GACAG,EAAAP,IACAK,EAAAG,KAAAD,EAAAP,GAAA,IAEAO,EAAAP,GAAA,EAEA,IAAAD,KAAAG,EACAO,OAAAC,UAAAC,eAAAC,KAAAV,EAAAH,KACAc,EAAAd,GAAAG,EAAAH,IAIA,IADAe,KAAAhB,GACAO,EAAAC,QACAD,EAAAU,OAAAV,GAOA,OAHAW,EAAAR,KAAAS,MAAAD,EAAAb,OAGAe,IAEA,SAAAA,IAEA,IADA,IAAAC,EACAf,EAAA,EAAiBA,EAAAY,EAAAV,OAA4BF,IAAA,CAG7C,IAFA,IAAAgB,EAAAJ,EAAAZ,GACAiB,GAAA,EACAC,EAAA,EAAkBA,EAAAF,EAAAd,OAA2BgB,IAAA,CAC7C,IAAAC,EAAAH,EAAAE,GACA,IAAAf,EAAAgB,KAAAF,GAAA,GAEAA,IACAL,EAAAQ,OAAApB,IAAA,GACAe,EAAAM,IAAAC,EAAAN,EAAA,KAGA,OAAAD,EAIA,IAAAQ,KAKApB,GACAqB,EAAA,GAGAZ,KAGA,SAAAS,EAAA1B,GAGA,GAAA4B,EAAA5B,GACA,OAAA4B,EAAA5B,GAAA8B,QAGA,IAAAC,EAAAH,EAAA5B,IACAK,EAAAL,EACAgC,GAAA,EACAF,YAUA,OANAhB,EAAAd,GAAAa,KAAAkB,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAnB,EAGAY,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACA1B,OAAA6B,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAb,GACApB,OAAA6B,eAAAT,EAAA,cAAiDc,OAAA,KAIjDlB,EAAAmB,EAAA,SAAAd,GACA,IAAAM,EAAAN,KAAAe,WACA,WAA2B,OAAAf,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAS,EAAAC,GAAsD,OAAAtC,OAAAC,UAAAC,eAAAC,KAAAkC,EAAAC,IAGtDtB,EAAAuB,EAAA,GAEA,IAAAC,EAAAC,OAAA,oBAAAA,OAAA,wBACAC,EAAAF,EAAAzC,KAAA4C,KAAAH,GACAA,EAAAzC,KAAAX,EACAoD,IAAAI,QACA,QAAAjD,EAAA,EAAgBA,EAAA6C,EAAA3C,OAAuBF,IAAAP,EAAAoD,EAAA7C,IACvC,IAAAU,EAAAqC,EAIAnC,EAAAR,MAAA,QAEAU","file":"i18nMessages.min.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \tvar jsonpArray = window[\"studiofrontendJsonP\"] = window[\"studiofrontendJsonP\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// add entry module to deferred list\n \tdeferredModules.push([436,0]);\n \t// run deferred modules when ready\n \treturn checkDeferredModules();\n"],"sourceRoot":""}
1
+ {"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","r","value","n","__esModule","object","property","p","jsonpArray","window","oldJsonpFunction","bind","slice"],"mappings":"aACA,SAAAA,EAAAC,GAOA,IANA,IAKAC,EAAAC,EALAC,EAAAH,EAAA,GACAI,EAAAJ,EAAA,GACAK,EAAAL,EAAA,GAGAM,EAAA,EAAAC,KACQD,EAAAH,EAAAK,OAAoBF,IAC5BJ,EAAAC,EAAAG,GACAG,EAAAP,IACAK,EAAAG,KAAAD,EAAAP,GAAA,IAEAO,EAAAP,GAAA,EAEA,IAAAD,KAAAG,EACAO,OAAAC,UAAAC,eAAAC,KAAAV,EAAAH,KACAc,EAAAd,GAAAG,EAAAH,IAIA,IADAe,KAAAhB,GACAO,EAAAC,QACAD,EAAAU,OAAAV,GAOA,OAHAW,EAAAR,KAAAS,MAAAD,EAAAb,OAGAe,IAEA,SAAAA,IAEA,IADA,IAAAC,EACAf,EAAA,EAAiBA,EAAAY,EAAAV,OAA4BF,IAAA,CAG7C,IAFA,IAAAgB,EAAAJ,EAAAZ,GACAiB,GAAA,EACAC,EAAA,EAAkBA,EAAAF,EAAAd,OAA2BgB,IAAA,CAC7C,IAAAC,EAAAH,EAAAE,GACA,IAAAf,EAAAgB,KAAAF,GAAA,GAEAA,IACAL,EAAAQ,OAAApB,IAAA,GACAe,EAAAM,IAAAC,EAAAN,EAAA,KAGA,OAAAD,EAIA,IAAAQ,KAKApB,GACAqB,EAAA,GAGAZ,KAGA,SAAAS,EAAA1B,GAGA,GAAA4B,EAAA5B,GACA,OAAA4B,EAAA5B,GAAA8B,QAGA,IAAAC,EAAAH,EAAA5B,IACAK,EAAAL,EACAgC,GAAA,EACAF,YAUA,OANAhB,EAAAd,GAAAa,KAAAkB,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAnB,EAGAY,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACA1B,OAAA6B,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAb,GACApB,OAAA6B,eAAAT,EAAA,cAAiDc,OAAA,KAIjDlB,EAAAmB,EAAA,SAAAd,GACA,IAAAM,EAAAN,KAAAe,WACA,WAA2B,OAAAf,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAS,EAAAC,GAAsD,OAAAtC,OAAAC,UAAAC,eAAAC,KAAAkC,EAAAC,IAGtDtB,EAAAuB,EAAA,GAEA,IAAAC,EAAAC,OAAA,oBAAAA,OAAA,wBACAC,EAAAF,EAAAzC,KAAA4C,KAAAH,GACAA,EAAAzC,KAAAX,EACAoD,IAAAI,QACA,QAAAjD,EAAA,EAAgBA,EAAA6C,EAAA3C,OAAuBF,IAAAP,EAAAoD,EAAA7C,IACvC,IAAAU,EAAAqC,EAIAnC,EAAAR,MAAA,QAEAU","file":"i18nMessages.min.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \tvar jsonpArray = window[\"studiofrontendJsonP\"] = window[\"studiofrontendJsonP\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// add entry module to deferred list\n \tdeferredModules.push([434,0]);\n \t// run deferred modules when ready\n \treturn checkDeferredModules();\n"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learningtribes-studio-frontend",
3
- "version": "1.4.2-alpha.3",
3
+ "version": "1.4.2-alpha.5",
4
4
  "description": "The frontend for the Open edX platform",
5
5
  "repository": "Learningtribes/studio-frontend",
6
6
  "scripts": {
@@ -1,105 +1,139 @@
1
1
  @import 'edx-bootstrap';
2
2
 
3
- // Matches the shared upload widget used by the Uploads page and PDF editor.
3
+ // Matches the shared upload widget used by the image modal.
4
4
  $upload-border: #ebebf1;
5
5
  $upload-brand: #5959eb;
6
6
  $upload-prompt-primary: #3535cd;
7
7
  $upload-prompt-secondary: #1f1f59;
8
8
  $upload-icon: #4c4c8e;
9
9
 
10
- .center-text {
11
- text-align: center !important;
12
- }
10
+ .assets-drop-zone {
11
+ .center-text {
12
+ text-align: center !important;
13
+ }
13
14
 
14
- .drop-zone-compact {
15
- border: 1px dashed $upload-border;
16
- border-radius: 8px;
17
- padding: 20px;
18
- display: flex;
19
- flex-direction: column;
20
- align-items: center;
21
- justify-content: center;
22
- text-align: center;
23
- cursor: pointer;
24
- background-color: #fff;
25
- transition: all 0.3s ease;
26
-
27
- &:hover {
28
- border-color: $upload-brand;
29
- background-color: #fff;
15
+ .drop-zone {
16
+ border: $dark dashed 1px;
17
+ padding: 0;
18
+
19
+ button {
20
+ white-space: normal;
21
+ margin-top: $spacer;
22
+ margin-bottom: $spacer;
23
+ }
24
+
25
+ h2 {
26
+ text-align: center !important;
27
+ font-weight: $font-weight-bold;
28
+ font-size: $font-size-lg;
29
+ margin-bottom: 0;
30
+ }
31
+
32
+ p {
33
+ font-size: $font-size-sm;
34
+ margin-bottom: $spacer * 0.5;
35
+ }
36
+
37
+ @media (min-width: map-get($grid-breakpoints, 'md')) {
38
+ padding: $spacer;
39
+
40
+ h2 {
41
+ margin-bottom: inherit;
42
+ }
43
+ }
44
+
45
+ &.drop-active {
46
+ outline: $dark dashed 2px;
47
+ background-color: rgba($primary, 0.2);
48
+ }
30
49
  }
31
- }
32
50
 
33
- .drop-zone {
34
- @extend .drop-zone-compact;
35
- }
51
+ .drop-zone-compact {
52
+ border: 1px dashed $upload-border;
53
+ border-radius: 8px;
54
+ padding: 20px;
55
+ display: flex;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ justify-content: center;
59
+ text-align: center;
60
+ cursor: pointer;
61
+ background-color: #fff;
62
+ transition: all 0.3s ease;
36
63
 
37
- .drop-active {
38
- outline: none;
39
- border-color: $upload-brand;
40
- background-color: #fff;
41
- }
64
+ &:hover {
65
+ border-color: $upload-brand;
66
+ background-color: #fff;
67
+ }
42
68
 
43
- .upload-icon {
44
- font-size: 32px;
45
- width: 32px;
46
- height: 32px;
47
- color: $upload-icon;
48
- margin-bottom: 8px;
49
- line-height: 1;
69
+ &.drop-active {
70
+ outline: none;
71
+ border-color: $upload-brand;
72
+ background-color: #fff;
73
+ }
74
+ }
50
75
 
51
- i {
76
+ .upload-icon {
52
77
  font-size: 32px;
78
+ width: 32px;
79
+ height: 32px;
80
+ color: $upload-icon;
81
+ margin-bottom: 8px;
53
82
  line-height: 1;
54
- display: block;
83
+
84
+ i {
85
+ font-size: 32px;
86
+ line-height: 1;
87
+ display: block;
88
+ font-style: normal;
89
+ }
55
90
  }
56
- }
57
91
 
58
- .upload-prompt {
59
- font-size: 16px;
60
- font-weight: 600;
61
- margin-bottom: 4px;
62
- }
92
+ .upload-prompt {
93
+ font-size: 16px;
94
+ font-weight: 600;
95
+ margin-bottom: 4px;
96
+ }
63
97
 
64
- .upload-prompt-primary {
65
- color: $upload-prompt-primary;
66
- cursor: pointer;
67
- }
98
+ .upload-prompt-primary {
99
+ color: $upload-prompt-primary;
100
+ cursor: pointer;
101
+ }
68
102
 
69
- .upload-prompt-secondary {
70
- color: $upload-prompt-secondary;
71
- font-size: 16px;
72
- font-weight: 600;
73
- }
103
+ .upload-prompt-secondary {
104
+ color: $upload-prompt-secondary;
105
+ font-size: 16px;
106
+ font-weight: 600;
107
+ }
74
108
 
75
- .upload-info {
76
- font-size: 14px;
77
- color: $upload-prompt-secondary;
78
- }
109
+ .upload-info {
110
+ font-size: 14px;
111
+ color: $upload-prompt-secondary;
112
+ }
79
113
 
80
- .supported-files-types {
81
- color: #727272;
82
- font-size: 12px !important;
83
- font-weight: 400;
84
- }
114
+ .supported-files-types {
115
+ color: #727272;
116
+ font-size: 12px !important;
117
+ font-weight: 400;
118
+ }
85
119
 
86
- .image-size-recommendation {
87
- color: #727272;
88
- font-size: 12px !important;
89
- font-weight: 400;
90
- }
120
+ .image-size-recommendation {
121
+ color: #727272;
122
+ font-size: 12px !important;
123
+ font-weight: 400;
124
+ }
91
125
 
92
- // Collapsed zone in image modal
93
- .collapsedZoneStyle {
94
- .drop-zone-compact,
95
- .drop-zone {
96
- padding: 20px 16px;
126
+ &.collapsedZoneStyle {
127
+ .drop-zone-compact,
128
+ .drop-zone {
129
+ padding: 20px 16px;
97
130
 
98
- [data-identifier="asset-drop-zone-icon"] {
99
- margin-bottom: 8px;
131
+ [data-identifier="asset-drop-zone-icon"] {
132
+ margin-bottom: 8px;
100
133
 
101
- span {
102
- font-size: 28px;
134
+ span {
135
+ font-size: 28px;
136
+ }
103
137
  }
104
138
  }
105
139
  }
@@ -27,7 +27,7 @@ describe('<AssetsDropZone />', () => {
27
27
 
28
28
  describe('renders', () => {
29
29
  it('contains file browser button', () => {
30
- expect(wrapper.find('[data-identifier="asset-drop-zone-browse-button"]').text()).toEqual('Click to upload');
30
+ expect(wrapper.find('[data-identifier="asset-drop-zone-browse-button"]').text()).toEqual('Select from your computer');
31
31
  });
32
32
  it('hides the icon & upload file size from screen readers', () => {
33
33
  expect(wrapper.find('[aria-hidden=true]').length).toBe(2);
@@ -42,6 +42,8 @@ describe('<AssetsDropZone />', () => {
42
42
  compactStyle: true,
43
43
  });
44
44
  expect(wrapper.find(dropZoneID).at(0).hasClass('drop-zone-compact')).toEqual(true);
45
+ expect(wrapper.find('[data-identifier="asset-drop-zone-browse-button"]').text()).toEqual('Click to upload');
46
+ expect(wrapper.find('[data-identifier="asset-drop-zone-header"]').text()).toEqual(' or drag and drop');
45
47
  });
46
48
  });
47
49
 
@@ -8,14 +8,24 @@ const messages = defineMessages({
8
8
  },
9
9
  assetsDropZoneBrowseLabel: {
10
10
  id: 'assetsDropZoneBrowseLabel',
11
- defaultMessage: 'Click to upload',
11
+ defaultMessage: 'Select from your computer',
12
12
  description: 'Upload label for the "Browse" button',
13
13
  },
14
14
  assetsDropZoneHeader: {
15
15
  id: 'assetsDropZoneHeader',
16
- defaultMessage: 'or drag and drop',
16
+ defaultMessage: 'Drag & Drop',
17
17
  description: 'Upload with dropzone section heading',
18
18
  },
19
+ assetsDropZoneClickToUpload: {
20
+ id: 'assetsDropZoneClickToUpload',
21
+ defaultMessage: 'Click to upload',
22
+ description: 'Primary prompt on the compact image upload zone',
23
+ },
24
+ assetsDropZoneOrDragAndDrop: {
25
+ id: 'assetsDropZoneOrDragAndDrop',
26
+ defaultMessage: 'or drag and drop',
27
+ description: 'Secondary prompt on the compact image upload zone',
28
+ },
19
29
  assetsDropZoneMaxFileSizeLabel: {
20
30
  id: 'assetsDropZoneMaxFileSizeLabel',
21
31
  defaultMessage: 'Size limit: {maxFileSizeMB} MB',
@@ -1,6 +1,8 @@
1
+ import { Button } from '@edx/paragon';
1
2
  import React from 'react';
2
3
  import classNames from 'classnames';
3
4
  import Dropzone from 'react-dropzone';
5
+ import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
4
6
  import PropTypes from 'prop-types';
5
7
 
6
8
  import WrappedMessage from '../../utils/i18n/formattedMessageWrapper';
@@ -32,11 +34,17 @@ export default class AssetsDropZone extends React.Component {
32
34
 
33
35
  setDropZoneRef = (ref) => {
34
36
  this.dropZoneRef = ref;
35
- if (this.props.buttonRef) {
37
+ if (this.props.compactStyle && this.props.buttonRef) {
36
38
  this.props.buttonRef(ref ? ref.node : null);
37
39
  }
38
40
  };
39
41
 
42
+ setUploadButtonRef = (ref) => {
43
+ if (this.props.buttonRef) {
44
+ this.props.buttonRef(ref);
45
+ }
46
+ };
47
+
40
48
  handleKeyDown = (event) => {
41
49
  if (event.key === 'Enter' || event.key === ' ') {
42
50
  event.preventDefault();
@@ -48,97 +56,171 @@ export default class AssetsDropZone extends React.Component {
48
56
  this.dropZoneRef.open();
49
57
  };
50
58
 
59
+ renderCompactZone = () => (
60
+ <Dropzone
61
+ accept={this.props.acceptedFileTypes}
62
+ activeClassName={styles['drop-active']}
63
+ aria-describedby="asset-drop-zone-max-file-size-label"
64
+ className={styles['drop-zone-compact']}
65
+ data-identifier="asset-drop-zone"
66
+ maxSize={this.dropZoneMaxFileSizeBytes}
67
+ onDrop={this.onDrop}
68
+ onKeyDown={this.handleKeyDown}
69
+ ref={this.setDropZoneRef}
70
+ role="button"
71
+ tabIndex="0"
72
+ >
73
+ <div
74
+ aria-hidden="true"
75
+ className={styles['upload-icon']}
76
+ data-identifier="asset-drop-zone-icon"
77
+ >
78
+ <i className="far fa-images" />
79
+ </div>
80
+ <div className={styles['upload-prompt']}>
81
+ <WrappedMessage message={messages.assetsDropZoneClickToUpload}>
82
+ { displayText => (
83
+ <span
84
+ className={styles['upload-prompt-primary']}
85
+ data-identifier="asset-drop-zone-browse-button"
86
+ >
87
+ {displayText}
88
+ </span>
89
+ )}
90
+ </WrappedMessage>
91
+ <WrappedMessage message={messages.assetsDropZoneOrDragAndDrop}>
92
+ { displayText => (
93
+ <span className={styles['upload-prompt-secondary']} data-identifier="asset-drop-zone-header"> {displayText}</span>
94
+ )}
95
+ </WrappedMessage>
96
+ </div>
97
+ {this.renderFileSizeLabel(styles['upload-info'])}
98
+ {this.renderExtraFileInfo()}
99
+ </Dropzone>
100
+ );
101
+
102
+ renderDefaultZone = () => (
103
+ <Dropzone
104
+ accept={this.props.acceptedFileTypes}
105
+ activeClassName={styles['drop-active']}
106
+ className={classNames(styles['drop-zone'], styles['center-text'])}
107
+ data-identifier="asset-drop-zone"
108
+ disableClick
109
+ maxSize={this.dropZoneMaxFileSizeBytes}
110
+ onDrop={this.onDrop}
111
+ ref={this.setDropZoneRef}
112
+ >
113
+ <div className={styles['center-text']} data-identifier="asset-drop-zone-icon">
114
+ <span
115
+ aria-hidden
116
+ className={classNames(
117
+ 'fad fa-cloud-upload-alt',
118
+ styles['center-text'],
119
+ FontAwesomeStyles['fa-3x'],
120
+ )}
121
+ />
122
+ </div>
123
+ <WrappedMessage message={messages.assetsDropZoneHeader}>
124
+ { displayText => (
125
+ <h2 data-identifier="asset-drop-zone-header">{displayText}</h2>
126
+ )}
127
+ </WrappedMessage>
128
+ <span className="label-or">or</span>
129
+ <div className={styles['center-text']}>
130
+ <WrappedMessage message={messages.assetsDropZoneBrowseLabel}>
131
+ { displayText => (
132
+ <Button
133
+ aria-describedby="asset-drop-zone-max-file-size-label"
134
+ className={['btn', 'btn-outline-primary']}
135
+ data-identifier="asset-drop-zone-browse-button"
136
+ label={displayText}
137
+ onClick={this.handleClick}
138
+ inputRef={this.setUploadButtonRef}
139
+ />
140
+ )}
141
+ </WrappedMessage>
142
+ </div>
143
+ {this.renderFileSizeLabel(styles['center-text'], 'p')}
144
+ {this.renderExtraFileInfo()}
145
+ </Dropzone>
146
+ );
147
+
148
+ renderFileSizeLabel = (className, Tag = 'div') => (
149
+ <WrappedMessage
150
+ message={messages.assetsDropZoneMaxFileSizeLabel}
151
+ values={{
152
+ maxFileSizeMB: this.props.maxFileSizeMB,
153
+ }}
154
+ >
155
+ { displayText => (
156
+ <Tag
157
+ aria-hidden
158
+ className={className}
159
+ data-identifier="asset-drop-zone-max-file-size-label"
160
+ id="asset-drop-zone-max-file-size-label"
161
+ >
162
+ {displayText}
163
+ </Tag>
164
+ )}
165
+ </WrappedMessage>
166
+ );
167
+
168
+ renderExtraFileInfo = () => (
169
+ <React.Fragment>
170
+ {this.props.supportedFileTypes !== undefined &&
171
+ <WrappedMessage
172
+ message={messages.assetsDropZoneSupportedFileTypesLabel}
173
+ values={{ filesTypesList: this.props.supportedFileTypes }}
174
+ >
175
+ { displayText => (
176
+ <p
177
+ aria-hidden
178
+ className={`${styles['center-text']} supported-files-types`}
179
+ data-identifier="asset-drop-zone-supported-file-types-label"
180
+ id="asset-drop-zone-supported-file-types-label"
181
+ >
182
+ {displayText}
183
+ </p>
184
+ )}
185
+ </WrappedMessage>
186
+ }
187
+ {this.props.imageSize !== undefined &&
188
+ <WrappedMessage
189
+ message={messages.assetsDropZoneImageSizeRecommandationLabel}
190
+ values={{ imageSize: this.props.imageSize }}
191
+ >
192
+ { displayText => (
193
+ <p
194
+ aria-hidden
195
+ className={`${styles['center-text']} image-size-recommendation`}
196
+ data-identifier="asset-drop-zone-image-size-recommandation-label"
197
+ id="asset-drop-zone-image-size-recommandation-label"
198
+ >
199
+ {displayText}
200
+ </p>
201
+ )}
202
+ </WrappedMessage>
203
+ }
204
+ </React.Fragment>
205
+ );
206
+
51
207
  render() {
52
208
  return (
53
- <WrappedMessage message={messages.assetsDropZoneAriaLabel} collapseBorder={this.props.collapseBorder}>
209
+ <WrappedMessage
210
+ message={messages.assetsDropZoneAriaLabel}
211
+ collapseBorder={this.props.collapseBorder}
212
+ >
54
213
  { regionLabel =>
55
214
  (
56
- <div role="region" aria-label={regionLabel} className={this.props.collapseBorder ? "collapsedZoneStyle": ""}>
57
- <Dropzone
58
- accept={this.props.acceptedFileTypes}
59
- activeClassName={styles['drop-active']}
60
- aria-describedby="asset-drop-zone-max-file-size-label"
61
- className={classNames([
62
- this.props.compactStyle ? styles['drop-zone-compact'] : styles['drop-zone'],
63
- ])}
64
- data-identifier="asset-drop-zone"
65
- maxSize={this.dropZoneMaxFileSizeBytes}
66
- onDrop={this.onDrop}
67
- onKeyDown={this.handleKeyDown}
68
- ref={this.setDropZoneRef}
69
- role="button"
70
- tabIndex="0"
71
- >
72
- <div
73
- aria-hidden="true"
74
- className={styles['upload-icon']}
75
- data-identifier="asset-drop-zone-icon"
76
- >
77
- <i className="far fa-images" />
78
- </div>
79
- <div className={styles['upload-prompt']}>
80
- <WrappedMessage message={messages.assetsDropZoneBrowseLabel} >
81
- { displayText => (
82
- <span
83
- className={styles['upload-prompt-primary']}
84
- data-identifier="asset-drop-zone-browse-button"
85
- >
86
- {displayText}
87
- </span>
88
- )}
89
- </WrappedMessage>
90
- <WrappedMessage message={messages.assetsDropZoneHeader}>
91
- { displayText => (
92
- <span className={styles['upload-prompt-secondary']} data-identifier="asset-drop-zone-header"> {displayText}</span>
93
- )}
94
- </WrappedMessage>
95
- </div>
96
- <WrappedMessage
97
- message={messages.assetsDropZoneMaxFileSizeLabel}
98
- values={{
99
- maxFileSizeMB: this.props.maxFileSizeMB,
100
- }}
101
- >
102
- { displayText => (
103
- <div
104
- aria-hidden="true"
105
- className={styles['upload-info']}
106
- data-identifier="asset-drop-zone-max-file-size-label"
107
- id="asset-drop-zone-max-file-size-label"
108
- >
109
- {displayText}
110
- </div>
111
- )}
112
- </WrappedMessage>
113
- {this.props.supportedFileTypes !== undefined &&
114
- <WrappedMessage message={messages.assetsDropZoneSupportedFileTypesLabel} values={{filesTypesList: this.props.supportedFileTypes}}>
115
- { displayText => (
116
- <p
117
- aria-hidden
118
- className={styles['center-text'] + " supported-files-types"}
119
- data-identifier="asset-drop-zone-supported-file-types-label"
120
- id="asset-drop-zone-supported-file-types-label"
121
- >
122
- {displayText}
123
- </p>
124
- )}
125
- </WrappedMessage>
126
- }
127
- {this.props.imageSize !== undefined &&
128
- <WrappedMessage message={messages.assetsDropZoneImageSizeRecommandationLabel} values={{imageSize: this.props.imageSize}}>
129
- { displayText => (
130
- <p
131
- aria-hidden
132
- className={styles['center-text'] + " image-size-recommendation"}
133
- data-identifier="asset-drop-zone-image-size-recommandation-label"
134
- id="asset-drop-zone-image-size-recommandation-label"
135
- >
136
- {displayText}
137
- </p>
138
- )}
139
- </WrappedMessage>
140
- }
141
- </Dropzone>
215
+ <div
216
+ role="region"
217
+ aria-label={regionLabel}
218
+ className={classNames(
219
+ styles['assets-drop-zone'],
220
+ { [styles.collapsedZoneStyle]: this.props.collapseBorder },
221
+ )}
222
+ >
223
+ {this.props.compactStyle ? this.renderCompactZone() : this.renderDefaultZone()}
142
224
  </div>
143
225
  )
144
226
  }
@@ -150,6 +232,7 @@ export default class AssetsDropZone extends React.Component {
150
232
  AssetsDropZone.propTypes = {
151
233
  acceptedFileTypes: PropTypes.string,
152
234
  buttonRef: PropTypes.func,
235
+ collapseBorder: PropTypes.bool,
153
236
  compactStyle: PropTypes.bool,
154
237
  courseDetails: PropTypes.shape({
155
238
  lang: PropTypes.string,
@@ -161,8 +244,10 @@ AssetsDropZone.propTypes = {
161
244
  id: PropTypes.string,
162
245
  revision: PropTypes.string,
163
246
  }).isRequired,
247
+ imageSize: PropTypes.string,
164
248
  maxFileCount: PropTypes.number,
165
249
  maxFileSizeMB: PropTypes.number,
250
+ supportedFileTypes: PropTypes.string,
166
251
  uploadAssets: PropTypes.func.isRequired,
167
252
  uploadExceedMaxCount: PropTypes.func.isRequired,
168
253
  uploadExceedMaxSize: PropTypes.func.isRequired,
@@ -173,7 +258,10 @@ AssetsDropZone.propTypes = {
173
258
  AssetsDropZone.defaultProps = {
174
259
  acceptedFileTypes: undefined,
175
260
  buttonRef: () => {},
261
+ collapseBorder: undefined,
176
262
  compactStyle: false,
263
+ imageSize: undefined,
177
264
  maxFileCount: 1000,
178
265
  maxFileSizeMB: 10,
266
+ supportedFileTypes: undefined,
179
267
  };
@@ -38,9 +38,8 @@ $badge-fg: #1717ab;
38
38
  background: #b0b0cd;
39
39
  }
40
40
  }
41
- }
42
41
 
43
- .asset-grid-item {
42
+ .asset-grid-item {
44
43
  flex-shrink: 0;
45
44
  width: 100px;
46
45
  cursor: pointer;
@@ -182,3 +181,4 @@ ol {
182
181
  .unbulleted-list-item {
183
182
  list-style: none;
184
183
  }
184
+ }