diff --git a/amd/build/data.min.js b/amd/build/data.min.js
index fe7797a..d8b6dba 100644
--- a/amd/build/data.min.js
+++ b/amd/build/data.min.js
@@ -1,3 +1,3 @@
-define("tiny_elements/data",["exports","core/str","tiny_elements/common","./variantslib","./helper","core/ajax","core/log"],(function(_exports,_str,_common,_variantslib,_helper,_ajax,_log){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_log=(obj=_log)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(contextid,userStudent,previewElements,canManage){_defineProperty(this,"categories",[]),_defineProperty(this,"components",[]),_defineProperty(this,"flavors",[]),_defineProperty(this,"variants",[]),_defineProperty(this,"langStrings",{}),_defineProperty(this,"userStudent",!1),_defineProperty(this,"canManage",!1),_defineProperty(this,"contextid",1),this.contextid=contextid,this.userStudent=userStudent,this.previewElements=previewElements,this.canManage=canManage,(0,_variantslib.setData)(this)}async loadData(){await this.loadElementsData(),this.langStrings=await this.getAllStrings()}getComponents(){return this.components}getFlavors(){return this.flavors}getVariants(){return this.variants}getComponentById(id){return(0,_helper.findById)(this.components,id)}getCategoryFlavors(categoryname){const categoryFlavors=[];return this.flavors.forEach((flavor=>{flavor.categoryname==categoryname&&categoryFlavors.push({id:flavor.id,name:flavor.name,displayname:flavor.displayname,displayorder:flavor.displayorder})})),categoryFlavors}getCategories(){const cats=[];return this.categories.forEach((category=>{let categoryFlavors=this.getCategoryFlavors(category.name);categoryFlavors.sort(((a,b)=>a.displayorder-b.displayorder));let hasFlavors=Array.isArray(categoryFlavors)&&categoryFlavors.length;cats.push({categoryid:category.id,name:category.displayname,categoryname:category.name,type:category.id,displayorder:category.displayorder,flavors:categoryFlavors,hasFlavors:hasFlavors,active:""})})),cats.sort(((a,b)=>a.displayorder-b.displayorder)),cats.length>0&&(cats[0].active="active",cats[0].flavors.length>0&&(cats[0].flavors[0].factive="active")),cats}getComponentVariants(component){const componentVariants=[];return component.variants.forEach((variant=>{let variantitem=(0,_helper.findByName)(this.variants,variant);if(void 0!==variantitem){let state=(0,_variantslib.variantExists)(component.name,variantitem.name)?"on":"off";componentVariants.push({id:variantitem.id,name:variantitem.name,displayname:variantitem.displayname,state:state,imageClass:variantitem.name+"-variant-"+state,variantclass:(variantitem.c4lcompatibility?"c4l":"elements")+"-"+variantitem.name+"-variant",title:this.langStrings.get(variantitem.name),content:variantitem.content})}})),componentVariants.sort(((a,b)=>a.name.localeCompare(b.name))),componentVariants}getCategoryById(id){return(0,_helper.findById)(this.categories,id)}getLangString(id){return this.langStrings.get(id)}getButtons(editor){const buttons=[];editor.selection.getContent();return Object.values(this.components).forEach((component=>{buttons.push({id:component.id,name:component.displayname,type:component.categoryname,imageClass:"elements-"+component.name+"-icon",htmlcode:component.code,variants:this.getComponentVariants(component),flavorlist:component.flavors.join(","),category:component.categoryname,displayorder:component.displayorder})})),buttons.sort(((a,b)=>a.displayorder-b.displayorder)),buttons}getTemplateContext(editor){return Object.assign({},{elementid:editor.id,buttons:this.getButtons(editor),categories:this.getCategories(),preview:this.previewElements,canmanage:this.canManage})}getPreviewElements(){return this.previewElements}async getAllStrings(){const keys=[],compRegex=/{{#([^}]*)}}/g;this.components.forEach((element=>{[...element.code.matchAll(compRegex)].forEach((strLang=>{-1===keys.indexOf(strLang[1])&&keys.push(strLang[1])})),[...element.text.matchAll(compRegex)].forEach((strLang=>{-1===keys.indexOf(strLang[1])&&keys.push(strLang[1])}))}));const stringValues=await(0,_str.get_strings)(keys.map((key=>({key:key,pluginname:_common.component}))));return new Map(keys.map(((key,index)=>[key,stringValues[index]])))}async loadElementsData(){const data=await(0,_ajax.call)([{methodname:"tiny_elements_get_elements_data",args:{isstudent:this.userStudent,contextid:this.contextid}}])[0].catch((err=>{_log.default.error(err.message)})),indexedComponents=[];data.components.forEach((component=>{indexedComponents[component.id]=component}));const indexedVariants=[];data.variants.forEach((variant=>{indexedVariants[variant.id]=variant}));const indexedCategories=[];data.categories.forEach((category=>{indexedCategories[category.id]=category})),this.components=indexedComponents,this.variants=indexedVariants,this.categories=indexedCategories,this.flavors=data.flavors}},_exports.default}));
+define("tiny_elements/data",["exports","core/str","tiny_elements/common","./variantslib","./helper","core/ajax","core/log"],(function(_exports,_str,_common,_variantslib,_helper,_ajax,_log){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_log=(obj=_log)&&obj.__esModule?obj:{default:obj};return _exports.default=class{constructor(contextid,userStudent,previewElements,canManage){_defineProperty(this,"categories",[]),_defineProperty(this,"components",[]),_defineProperty(this,"flavors",[]),_defineProperty(this,"variants",[]),_defineProperty(this,"langStrings",{}),_defineProperty(this,"userStudent",!1),_defineProperty(this,"canManage",!1),_defineProperty(this,"contextid",1),this.contextid=contextid,this.userStudent=userStudent,this.previewElements=previewElements,this.canManage=canManage,(0,_variantslib.setData)(this)}async loadData(){await this.loadElementsData(),this.langStrings=await this.getAllStrings()}getComponents(){return this.components}getFlavors(){return this.flavors}getVariants(){return this.variants}getComponentById(id){return(0,_helper.findById)(this.components,id)}getCategoryFlavors(categoryname){const categoryFlavors=[];return this.flavors.forEach((flavor=>{flavor.categoryname==categoryname&&categoryFlavors.push({id:flavor.id,name:flavor.name,displayname:flavor.displayname,displayorder:flavor.displayorder})})),categoryFlavors}getCategories(){const cats=[],flexbasis=this.setFlexbasis();return this.categories.forEach((category=>{let categoryFlavors=this.getCategoryFlavors(category.name);categoryFlavors.sort(((a,b)=>a.displayorder-b.displayorder));let hasFlavors=Array.isArray(categoryFlavors)&&categoryFlavors.length,flexbasisrng=flexbasis;category.displayorder%2==0&&(flexbasisrng=flexbasis-(Math.floor(3*Math.random())+1)),cats.push({categoryid:category.id,name:category.displayname,categoryname:category.name,type:category.id,displayorder:this.categories.length-category.displayorder,flexbasis:flexbasisrng,flavors:categoryFlavors,hasFlavors:hasFlavors,active:""})})),cats.sort(((a,b)=>a.displayorder-b.displayorder)),cats.length>0&&(cats[0].active="active",cats[0].flavors.length>0&&(cats[0].flavors[0].factive="active")),cats}getComponentVariants(component){const componentVariants=[];return component.variants.forEach((variant=>{let variantitem=(0,_helper.findByName)(this.variants,variant);if(void 0!==variantitem){let state=(0,_variantslib.variantExists)(component.name,variantitem.name)?"on":"off";componentVariants.push({id:variantitem.id,name:variantitem.name,displayname:variantitem.displayname,state:state,imageClass:variantitem.name+"-variant-"+state,variantclass:(variantitem.c4lcompatibility?"c4l":"elements")+"-"+variantitem.name+"-variant",title:this.langStrings.get(variantitem.name),content:variantitem.content})}})),componentVariants.sort(((a,b)=>a.name.localeCompare(b.name))),componentVariants}getCategoryById(id){return(0,_helper.findById)(this.categories,id)}getLangString(id){return this.langStrings.get(id)}getButtons(editor){const buttons=[];editor.selection.getContent();return Object.values(this.components).forEach((component=>{buttons.push({id:component.id,name:component.displayname,type:component.categoryname,imageClass:"elements-"+component.name+"-icon",htmlcode:component.code,variants:this.getComponentVariants(component),flavorlist:component.flavors.join(","),category:component.categoryname,displayorder:component.displayorder})})),buttons.sort(((a,b)=>a.displayorder-b.displayorder)),buttons}getTemplateContext(editor){return Object.assign({},{elementid:editor.id,buttons:this.getButtons(editor),categories:this.getCategories(),preview:this.previewElements,canmanage:this.canManage})}getPreviewElements(){return this.previewElements}async getAllStrings(){const keys=[],compRegex=/{{#([^}]*)}}/g;this.components.forEach((element=>{[...element.code.matchAll(compRegex)].forEach((strLang=>{-1===keys.indexOf(strLang[1])&&keys.push(strLang[1])})),[...element.text.matchAll(compRegex)].forEach((strLang=>{-1===keys.indexOf(strLang[1])&&keys.push(strLang[1])}))}));const stringValues=await(0,_str.get_strings)(keys.map((key=>({key:key,pluginname:_common.component}))));return new Map(keys.map(((key,index)=>[key,stringValues[index]])))}async loadElementsData(){const data=await(0,_ajax.call)([{methodname:"tiny_elements_get_elements_data",args:{isstudent:this.userStudent,contextid:this.contextid}}])[0].catch((err=>{_log.default.error(err.message)})),indexedComponents=[];data.components.forEach((component=>{indexedComponents[component.id]=component}));const indexedVariants=[];data.variants.forEach((variant=>{indexedVariants[variant.id]=variant}));const indexedCategories=[];data.categories.forEach((category=>{indexedCategories[category.id]=category})),this.components=indexedComponents,this.variants=indexedVariants,this.categories=indexedCategories,this.flavors=data.flavors}setFlexbasis(){let rows=Math.round(this.categories.length/5);return rows<1&&(rows=1),100/Math.ceil(this.categories.length/rows)}},_exports.default}));
//# sourceMappingURL=data.min.js.map
\ No newline at end of file
diff --git a/amd/build/data.min.js.map b/amd/build/data.min.js.map
index ae934f2..7d2f73d 100644
--- a/amd/build/data.min.js.map
+++ b/amd/build/data.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"data.min.js","sources":["../src/data.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Container for tiny_elements data (categories, components, flavors, variants).\n *\n * @module tiny_elements/data\n * @copyright 2025 ISB Bayern\n * @author Stefan Hanauska \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_strings as getStrings} from 'core/str';\nimport {component as pluginname} from 'tiny_elements/common';\nimport {\n variantExists,\n setData as setVariantsData\n} from './variantslib';\nimport {\n findById,\n findByName\n} from './helper';\nimport {call as fetchMany} from 'core/ajax';\nimport Log from 'core/log';\n\nexport default class Data {\n categories = [];\n components = [];\n flavors = [];\n variants = [];\n langStrings = {};\n userStudent = false;\n canManage = false;\n contextid = 1;\n\n constructor(contextid, userStudent, previewElements, canManage) {\n this.contextid = contextid;\n this.userStudent = userStudent;\n this.previewElements = previewElements;\n this.canManage = canManage;\n setVariantsData(this);\n }\n\n async loadData() {\n await this.loadElementsData();\n this.langStrings = await this.getAllStrings();\n }\n\n getComponents() {\n return this.components;\n }\n\n getFlavors() {\n return this.flavors;\n }\n\n getVariants() {\n return this.variants;\n }\n\n getComponentById(id) {\n return findById(this.components, id);\n }\n\n getCategoryFlavors(categoryname) {\n const categoryFlavors = [];\n this.flavors.forEach(flavor => {\n if (flavor.categoryname == categoryname) {\n categoryFlavors.push({\n id: flavor.id,\n name: flavor.name,\n displayname: flavor.displayname,\n displayorder: flavor.displayorder,\n });\n }\n });\n return categoryFlavors;\n }\n\n /**\n * Get the Elements categories for the dialogue.\n *\n * @returns {object} data\n */\n getCategories() {\n const cats = [];\n // Iterate over contexts.\n this.categories.forEach((category) => {\n let categoryFlavors = this.getCategoryFlavors(category.name);\n categoryFlavors.sort((a, b) => a.displayorder - b.displayorder);\n let hasFlavors = Array.isArray(categoryFlavors) && categoryFlavors.length;\n cats.push({\n categoryid: category.id,\n name: category.displayname,\n categoryname: category.name,\n type: category.id,\n displayorder: category.displayorder,\n flavors: categoryFlavors,\n hasFlavors: hasFlavors,\n active: '',\n });\n });\n // Sort by displayorder and set first to active.\n cats.sort((a, b) => a.displayorder - b.displayorder);\n if (cats.length > 0) {\n cats[0].active = 'active';\n if (cats[0].flavors.length > 0) {\n cats[0].flavors[0].factive = 'active';\n }\n }\n\n return cats;\n }\n\n getComponentVariants(component) {\n const componentVariants = [];\n component.variants.forEach(variant => {\n let variantitem = findByName(this.variants, variant);\n if (variantitem !== undefined) {\n let state = variantExists(component.name, variantitem.name) ? 'on' : 'off';\n componentVariants.push({\n id: variantitem.id,\n name: variantitem.name,\n displayname: variantitem.displayname,\n state: state,\n imageClass: variantitem.name + '-variant-' + state,\n variantclass: (variantitem.c4lcompatibility ? 'c4l' : 'elements') + '-' + variantitem.name + '-variant',\n title: this.langStrings.get(variantitem.name),\n content: variantitem.content,\n });\n }\n });\n componentVariants.sort((a, b) => (a.name.localeCompare(b.name)));\n return componentVariants;\n }\n\n getCategoryById(id) {\n return findById(this.categories, id);\n }\n\n getLangString(id) {\n return this.langStrings.get(id);\n }\n\n /**\n * Get the Elements buttons for the dialogue.\n *\n * @param {Editor} editor\n * @returns {object} buttons\n */\n getButtons(editor) {\n const buttons = [];\n // Not used at the moment.\n // eslint-disable-next-line no-unused-vars\n const sel = editor.selection.getContent();\n Object.values(this.components).forEach(component => {\n buttons.push({\n id: component.id,\n name: component.displayname,\n type: component.categoryname,\n imageClass: 'elements-' + component.name + '-icon',\n htmlcode: component.code,\n variants: this.getComponentVariants(component),\n flavorlist: component.flavors.join(','),\n category: component.categoryname,\n displayorder: component.displayorder,\n });\n });\n buttons.sort((a, b) => a.displayorder - b.displayorder);\n\n return buttons;\n }\n\n /**\n * Get the template context for the dialogue.\n *\n * @param {Editor} editor\n * @returns {object} data\n */\n getTemplateContext(editor) {\n return Object.assign({}, {\n elementid: editor.id,\n buttons: this.getButtons(editor),\n categories: this.getCategories(),\n preview: this.previewElements,\n canmanage: this.canManage,\n });\n }\n\n getPreviewElements() {\n return this.previewElements;\n }\n\n /**\n * Get language strings.\n *\n * @return {object} Language strings\n */\n async getAllStrings() {\n const keys = [];\n const compRegex = /{{#([^}]*)}}/g;\n\n this.components.forEach(element => {\n // Get lang strings from components.\n [...element.code.matchAll(compRegex)].forEach(strLang => {\n if (keys.indexOf(strLang[1]) === -1) {\n keys.push(strLang[1]);\n }\n });\n\n // Get lang strings from text placeholders.\n [...element.text.matchAll(compRegex)].forEach(strLang => {\n if (keys.indexOf(strLang[1]) === -1) {\n keys.push(strLang[1]);\n }\n });\n });\n\n const stringValues = await getStrings(keys.map((key) => ({key, pluginname})));\n return new Map(keys.map((key, index) => ([key, stringValues[index]])));\n }\n\n async loadElementsData() {\n const data = await fetchMany([{\n methodname: 'tiny_elements_get_elements_data',\n args: {\n isstudent: this.userStudent,\n contextid: this.contextid\n },\n }])[0].catch(err => {\n Log.error(err.message);\n });\n\n const indexedComponents = [];\n data.components.forEach(component => {\n indexedComponents[component.id] = component;\n });\n\n const indexedVariants = [];\n data.variants.forEach(variant => {\n indexedVariants[variant.id] = variant;\n });\n\n const indexedCategories = [];\n data.categories.forEach(category => {\n indexedCategories[category.id] = category;\n });\n\n this.components = indexedComponents;\n this.variants = indexedVariants;\n this.categories = indexedCategories;\n this.flavors = data.flavors;\n }\n}\n"],"names":["constructor","contextid","userStudent","previewElements","canManage","this","loadElementsData","langStrings","getAllStrings","getComponents","components","getFlavors","flavors","getVariants","variants","getComponentById","id","getCategoryFlavors","categoryname","categoryFlavors","forEach","flavor","push","name","displayname","displayorder","getCategories","cats","categories","category","sort","a","b","hasFlavors","Array","isArray","length","categoryid","type","active","factive","getComponentVariants","component","componentVariants","variant","variantitem","undefined","state","imageClass","variantclass","c4lcompatibility","title","get","content","localeCompare","getCategoryById","getLangString","getButtons","editor","buttons","selection","getContent","Object","values","htmlcode","code","flavorlist","join","getTemplateContext","assign","elementid","preview","canmanage","getPreviewElements","keys","compRegex","element","matchAll","strLang","indexOf","text","stringValues","map","key","pluginname","Map","index","data","methodname","args","isstudent","catch","err","error","message","indexedComponents","indexedVariants","indexedCategories"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"data.min.js","sources":["../src/data.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Container for tiny_elements data (categories, components, flavors, variants).\n *\n * @module tiny_elements/data\n * @copyright 2025 ISB Bayern\n * @author Stefan Hanauska \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_strings as getStrings} from 'core/str';\nimport {component as pluginname} from 'tiny_elements/common';\nimport {\n variantExists,\n setData as setVariantsData\n} from './variantslib';\nimport {\n findById,\n findByName\n} from './helper';\nimport {call as fetchMany} from 'core/ajax';\nimport Log from 'core/log';\n\nexport default class Data {\n categories = [];\n components = [];\n flavors = [];\n variants = [];\n langStrings = {};\n userStudent = false;\n canManage = false;\n contextid = 1;\n\n constructor(contextid, userStudent, previewElements, canManage) {\n this.contextid = contextid;\n this.userStudent = userStudent;\n this.previewElements = previewElements;\n this.canManage = canManage;\n setVariantsData(this);\n }\n\n async loadData() {\n await this.loadElementsData();\n this.langStrings = await this.getAllStrings();\n }\n\n getComponents() {\n return this.components;\n }\n\n getFlavors() {\n return this.flavors;\n }\n\n getVariants() {\n return this.variants;\n }\n\n getComponentById(id) {\n return findById(this.components, id);\n }\n\n getCategoryFlavors(categoryname) {\n const categoryFlavors = [];\n this.flavors.forEach(flavor => {\n if (flavor.categoryname == categoryname) {\n categoryFlavors.push({\n id: flavor.id,\n name: flavor.name,\n displayname: flavor.displayname,\n displayorder: flavor.displayorder,\n });\n }\n });\n return categoryFlavors;\n }\n\n /**\n * Get the Elements categories for the dialogue.\n *\n * @returns {object} data\n */\n getCategories() {\n const cats = [];\n const flexbasis = this.setFlexbasis();\n // Iterate over contexts.\n this.categories.forEach((category) => {\n let categoryFlavors = this.getCategoryFlavors(category.name);\n categoryFlavors.sort((a, b) => a.displayorder - b.displayorder);\n let hasFlavors = Array.isArray(categoryFlavors) && categoryFlavors.length;\n let flexbasisrng = flexbasis;\n if (category.displayorder % 2 == 0) {\n // Set flexbases minus 1,2 or 3 for visual purposes.\n flexbasisrng = flexbasis - (Math.floor(Math.random() * 3) + 1);\n }\n cats.push({\n categoryid: category.id,\n name: category.displayname,\n categoryname: category.name,\n type: category.id,\n displayorder: this.categories.length - category.displayorder,\n flexbasis: flexbasisrng,\n flavors: categoryFlavors,\n hasFlavors: hasFlavors,\n active: '',\n });\n });\n // Sort by displayorder and set first to active.\n cats.sort((a, b) => a.displayorder - b.displayorder);\n if (cats.length > 0) {\n cats[0].active = 'active';\n if (cats[0].flavors.length > 0) {\n cats[0].flavors[0].factive = 'active';\n }\n }\n\n return cats;\n }\n\n getComponentVariants(component) {\n const componentVariants = [];\n component.variants.forEach(variant => {\n let variantitem = findByName(this.variants, variant);\n if (variantitem !== undefined) {\n let state = variantExists(component.name, variantitem.name) ? 'on' : 'off';\n componentVariants.push({\n id: variantitem.id,\n name: variantitem.name,\n displayname: variantitem.displayname,\n state: state,\n imageClass: variantitem.name + '-variant-' + state,\n variantclass: (variantitem.c4lcompatibility ? 'c4l' : 'elements') + '-' + variantitem.name + '-variant',\n title: this.langStrings.get(variantitem.name),\n content: variantitem.content,\n });\n }\n });\n componentVariants.sort((a, b) => (a.name.localeCompare(b.name)));\n return componentVariants;\n }\n\n getCategoryById(id) {\n return findById(this.categories, id);\n }\n\n getLangString(id) {\n return this.langStrings.get(id);\n }\n\n /**\n * Get the Elements buttons for the dialogue.\n *\n * @param {Editor} editor\n * @returns {object} buttons\n */\n getButtons(editor) {\n const buttons = [];\n // Not used at the moment.\n // eslint-disable-next-line no-unused-vars\n const sel = editor.selection.getContent();\n Object.values(this.components).forEach(component => {\n buttons.push({\n id: component.id,\n name: component.displayname,\n type: component.categoryname,\n imageClass: 'elements-' + component.name + '-icon',\n htmlcode: component.code,\n variants: this.getComponentVariants(component),\n flavorlist: component.flavors.join(','),\n category: component.categoryname,\n displayorder: component.displayorder,\n });\n });\n buttons.sort((a, b) => a.displayorder - b.displayorder);\n\n return buttons;\n }\n\n /**\n * Get the template context for the dialogue.\n *\n * @param {Editor} editor\n * @returns {object} data\n */\n getTemplateContext(editor) {\n return Object.assign({}, {\n elementid: editor.id,\n buttons: this.getButtons(editor),\n categories: this.getCategories(),\n preview: this.previewElements,\n canmanage: this.canManage,\n });\n }\n\n getPreviewElements() {\n return this.previewElements;\n }\n\n /**\n * Get language strings.\n *\n * @return {object} Language strings\n */\n async getAllStrings() {\n const keys = [];\n const compRegex = /{{#([^}]*)}}/g;\n\n this.components.forEach(element => {\n // Get lang strings from components.\n [...element.code.matchAll(compRegex)].forEach(strLang => {\n if (keys.indexOf(strLang[1]) === -1) {\n keys.push(strLang[1]);\n }\n });\n\n // Get lang strings from text placeholders.\n [...element.text.matchAll(compRegex)].forEach(strLang => {\n if (keys.indexOf(strLang[1]) === -1) {\n keys.push(strLang[1]);\n }\n });\n });\n\n const stringValues = await getStrings(keys.map((key) => ({key, pluginname})));\n return new Map(keys.map((key, index) => ([key, stringValues[index]])));\n }\n\n async loadElementsData() {\n const data = await fetchMany([{\n methodname: 'tiny_elements_get_elements_data',\n args: {\n isstudent: this.userStudent,\n contextid: this.contextid\n },\n }])[0].catch(err => {\n Log.error(err.message);\n });\n\n const indexedComponents = [];\n data.components.forEach(component => {\n indexedComponents[component.id] = component;\n });\n\n const indexedVariants = [];\n data.variants.forEach(variant => {\n indexedVariants[variant.id] = variant;\n });\n\n const indexedCategories = [];\n data.categories.forEach(category => {\n indexedCategories[category.id] = category;\n });\n\n this.components = indexedComponents;\n this.variants = indexedVariants;\n this.categories = indexedCategories;\n this.flavors = data.flavors;\n }\n\n /**\n * Calculates flex-basis to space elements evenly.\n */\n setFlexbasis() {\n // 5 elements should fit in one row.\n let rows = Math.round(this.categories.length / 5);\n if (rows < 1) {\n rows = 1;\n }\n let elementsPerRow = Math.ceil(this.categories.length / rows);\n return 100 / elementsPerRow;\n }\n}\n"],"names":["constructor","contextid","userStudent","previewElements","canManage","this","loadElementsData","langStrings","getAllStrings","getComponents","components","getFlavors","flavors","getVariants","variants","getComponentById","id","getCategoryFlavors","categoryname","categoryFlavors","forEach","flavor","push","name","displayname","displayorder","getCategories","cats","flexbasis","setFlexbasis","categories","category","sort","a","b","hasFlavors","Array","isArray","length","flexbasisrng","Math","floor","random","categoryid","type","active","factive","getComponentVariants","component","componentVariants","variant","variantitem","undefined","state","imageClass","variantclass","c4lcompatibility","title","get","content","localeCompare","getCategoryById","getLangString","getButtons","editor","buttons","selection","getContent","Object","values","htmlcode","code","flavorlist","join","getTemplateContext","assign","elementid","preview","canmanage","getPreviewElements","keys","compRegex","element","matchAll","strLang","indexOf","text","stringValues","map","key","pluginname","Map","index","data","methodname","args","isstudent","catch","err","error","message","indexedComponents","indexedVariants","indexedCategories","rows","round","ceil"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/src/data.js b/amd/src/data.js
index a84fb57..2ccc220 100644
--- a/amd/src/data.js
+++ b/amd/src/data.js
@@ -96,17 +96,24 @@ export default class Data {
*/
getCategories() {
const cats = [];
+ const flexbasis = this.setFlexbasis();
// Iterate over contexts.
this.categories.forEach((category) => {
let categoryFlavors = this.getCategoryFlavors(category.name);
categoryFlavors.sort((a, b) => a.displayorder - b.displayorder);
let hasFlavors = Array.isArray(categoryFlavors) && categoryFlavors.length;
+ let flexbasisrng = flexbasis;
+ if (category.displayorder % 2 == 0) {
+ // Set flexbases minus 1,2 or 3 for visual purposes.
+ flexbasisrng = flexbasis - (Math.floor(Math.random() * 3) + 1);
+ }
cats.push({
categoryid: category.id,
name: category.displayname,
categoryname: category.name,
type: category.id,
- displayorder: category.displayorder,
+ displayorder: this.categories.length - category.displayorder,
+ flexbasis: flexbasisrng,
flavors: categoryFlavors,
hasFlavors: hasFlavors,
active: '',
@@ -263,4 +270,17 @@ export default class Data {
this.categories = indexedCategories;
this.flavors = data.flavors;
}
+
+ /**
+ * Calculates flex-basis to space elements evenly.
+ */
+ setFlexbasis() {
+ // 5 elements should fit in one row.
+ let rows = Math.round(this.categories.length / 5);
+ if (rows < 1) {
+ rows = 1;
+ }
+ let elementsPerRow = Math.ceil(this.categories.length / rows);
+ return 100 / elementsPerRow;
+ }
}
diff --git a/scss/_buttons.scss b/scss/_buttons.scss
index 786e223..744f87d 100644
--- a/scss/_buttons.scss
+++ b/scss/_buttons.scss
@@ -2,7 +2,12 @@
.elements-plugin-container {
.nav.nav-tabs {
- flex-wrap: wrap;
+ flex-wrap: wrap-reverse;
+ flex-direction: row-reverse;
+ justify-content: flex-end;
+ li {
+ margin-top: 1px;
+ }
.nav-item a.active {
background-color: #f6f7f8;
border-bottom: 1px solid #f6f7f8;
diff --git a/styles.css b/styles.css
index ab54216..e221221 100644
--- a/styles.css
+++ b/styles.css
@@ -1 +1 @@
-#page-lib-editor-tiny-plugins-elements-preview #page{padding-top:0;margin-top:0}#page-lib-editor-tiny-plugins-elements-management .col.item,#page-lib-editor-tiny-plugins-elements-printurls .col.item{min-width:250px;max-width:250px}#page-lib-editor-tiny-plugins-elements-management .col.item.addcontainer .card,#page-lib-editor-tiny-plugins-elements-printurls .col.item.addcontainer .card{background-color:#d3d3d3}#page-lib-editor-tiny-plugins-elements-management .tiny_elements_thumbnail,#page-lib-editor-tiny-plugins-elements-printurls .tiny_elements_thumbnail{width:48px;height:48px}#page-lib-editor-tiny-plugins-elements-management .preview.management,#page-lib-editor-tiny-plugins-elements-printurls .preview.management{display:flex;justify-content:space-evenly;width:100%;height:3rem;padding-bottom:.25rem}#page-lib-editor-tiny-plugins-elements-management .preview.management>div,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div{width:45%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div .elements-button-text,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div .elements-button-text{height:100%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div .elements-button-text::before,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div .elements-button-text::before{display:flex;width:unset;height:100%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more{display:flex;flex-direction:column;justify-content:center;width:10%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more .elements-button-text::before,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more .elements-button-text::before{all:unset;content:"..."}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more .elements-button-text,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more .elements-button-text{word-wrap:unset}#page-lib-editor-tiny-plugins-elements-management .compcat:hover .card,#page-lib-editor-tiny-plugins-elements-printurls .compcat:hover .card{border:1px solid #787878}#page-lib-editor-tiny-plugins-elements-management .compcat.active .card,#page-lib-editor-tiny-plugins-elements-printurls .compcat.active .card{border:1px solid #0b5190}#page-lib-editor-tiny-plugins-elements-management .card-body,#page-lib-editor-tiny-plugins-elements-printurls .card-body{display:flex;flex-direction:column;padding-top:.75rem}#page-lib-editor-tiny-plugins-elements-management .card-body>div:hover,#page-lib-editor-tiny-plugins-elements-printurls .card-body>div:hover{cursor:pointer}#page-lib-editor-tiny-plugins-elements-management .card-body>div:hover+a.edit,#page-lib-editor-tiny-plugins-elements-printurls .card-body>div:hover+a.edit{color:#094478}#page-lib-editor-tiny-plugins-elements-management form[data-formtype=tiny_elements_comp_flavor] input.form-control,#page-lib-editor-tiny-plugins-elements-printurls form[data-formtype=tiny_elements_comp_flavor] input.form-control{width:100%}#page-lib-editor-tiny-plugins-elements-management .compcat[data-compcat=found-items] a,#page-lib-editor-tiny-plugins-elements-printurls .compcat[data-compcat=found-items] a{display:none}#page-lib-editor-tiny-plugins-elements-management form[data-formtype=tiny_elements_editlicense] div[data-fieldtype=static],#page-lib-editor-tiny-plugins-elements-printurls form[data-formtype=tiny_elements_editlicense] div[data-fieldtype=static]{padding-top:calc(.75rem + 1px)}.elements-plugin-container .nav.nav-tabs{flex-wrap:wrap}.elements-plugin-container .nav.nav-tabs .nav-item a.active{background-color:#f6f7f8;border-bottom:1px solid #f6f7f8}.elements-plugin-container .nav.nav-tabs .nav-item a.active.dropdown-item{color:inherit}.elements-plugin-container .nav.nav-tabs,.elements-plugin-container .nav.nav-tabs .dropdown-item{font-size:.8rem}.elements-plugin-container .nav.nav-tabs .nav-link{border:1px solid #d3d3d3;border-bottom:none;border-top-right-radius:.2rem;border-top-left-radius:.2rem}.elements-buttons-preview{display:flex;justify-content:space-between;width:100%;min-height:31vh;max-height:60vh;background-color:#f6f7f8}.elements-buttons-grid{display:flex;flex-wrap:wrap;justify-content:start;row-gap:12px;column-gap:12px;margin-bottom:0;width:100%;overflow-y:auto}.elements-hidden{display:none !important;max-height:0;padding:0}.elementst-dialog-button{display:flex;flex-direction:row;align-items:center;justify-content:space-between;position:relative;height:48px;max-height:100px;width:100%;max-width:100%;background-color:#fff;border:1px solid #e4e9ec;color:#535d76;box-shadow:0 0 6px 0 rgba(0,0,0,.1);overflow-wrap:anywhere;overflow:hidden;padding:0;margin-bottom:8px}.elementst-dialog-button.elements-custom-icon .elements-button-text i{position:absolute;background-repeat:no-repeat;background-size:contain;background-position:center;min-height:23px;min-width:49px;top:auto;left:0;border-right:1px solid #dce3f0}.elementst-dialog-button .elements-button-text{display:flex;align-items:center;height:100%}.elementst-dialog-button .elements-button-text::before{position:absolute;left:0;height:100%;min-height:23px;display:flex;justify-content:center;align-items:center;border-right:1px solid #dce3f0;width:60px;height:60px;top:3px;align-content:center}.elementst-dialog-button:hover,.elementst-dialog-button:active{box-shadow:inset 0 0 3px rgba(22,121,249,.3)}.elementst-dialog-button .elements-button-text{position:relative;font-size:12px;font-weight:500;width:100%;text-align:left;padding-left:62px}.elements-code-preview{display:none;overflow:hidden;background-color:#fff;border-top:2px solid #f6f7f8}.elements-code-preview .elements-component-code{-webkit-transform:scale(0.6);-ms-transform:scale(0.6);transform:scale(0.6);width:160%;margin-top:35px;position:absolute}.elementst-dialog-button .elements-button-variants{display:grid;grid-auto-flow:column;grid-gap:3px;justify-content:end;width:100%;height:42px;margin-right:3px}.elementst-dialog-button .elements-button-variants .elements-button-variant{display:flex;justify-content:center;align-items:center;background-color:#ecf3ff;color:#1679f9;height:42px;width:36px}.elementst-dialog-button .elements-button-variants .elements-button-variant::before{content:" ";background-size:50%;background-repeat:no-repeat;background-position:center;height:42px;width:36px}.elementst-dialog-button .elements-button-variants .elements-button-variant.on,.elementst-dialog-button .elements-button-variants .elements-button-variant:hover{background-color:#1679f9;color:#fff}.elementst-dialog-button .elements-button-variants .elements-button-variant.on::before,.elementst-dialog-button .elements-button-variants .elements-button-variant:hover::before{filter:brightness(10)}body.mce-content-body .collapse:not(.show),.elements-code-preview .collapse:not(.show){display:block !important}@media only screen and (min-width: 576px){.elements-modal.modal-open .modal-dialog,.elements-modal-no-preview.modal-open .modal-dialog{min-width:550px}.elements-select-filters{display:none}.elements-buttons-filters,.elements-buttons-flavors{display:flex;justify-content:center;align-items:center;margin:10px 0 10px}}@media only screen and (min-width: 992px){.elements-modal-no-preview.modal-open .modal-dialog{max-width:550px}.elements-buttons-preview.elements-no-preview{justify-content:center}.elements-buttons-preview.elements-no-preview .elements-buttons-grid{grid-gap:9px;justify-content:center;width:405px}.elements-buttons-preview.elements-no-preview .elements-code-preview{display:none}.elements-buttons-grid{width:440px;padding:10px}.elementst-dialog-button.elements-custom-icon .elements-button-text i{top:20px;left:0;right:0;margin:0 auto;max-width:25px;min-width:25px;width:100%;border-right:none}.elementst-dialog-button{height:100px;width:116px;max-width:116px;box-shadow:none;border-radius:4px;margin-bottom:0}.elementst-dialog-button::before{content:""}.elementst-dialog-button .elements-button-text::before{left:0;right:0;margin:0 auto;border-right:none;display:inline-block;width:60px;height:60px;top:3px}.elementst-dialog-button .elements-button-text{padding:70px 8px 15px 8px;font-size:12px;font-weight:500;line-height:13px;width:80px;text-align:center;justify-content:center}.elementst-dialog-button .elements-button-variants{grid-auto-flow:row;grid-gap:0;align-content:flex-start;justify-content:start;width:36px;height:99px;background-color:#ecf3ff;margin:0}.elementst-dialog-button .elements-button-variants .elements-button-variant{display:flex;justify-content:center;align-items:center;color:#1679f9;height:33px;width:36px}.elementst-dialog-button:hover,.elementst-dialog-button:active{box-shadow:0 0 0 3px rgba(22,121,249,.3)}.elementst-dialog-button .elements-button-variants .elements-button-variant:first-child,.elementst-dialog-button .elements-button-variants .elements-button-variant:nth-child(2){border-bottom:1px solid #fff}.elementst-dialog-button:hover .elements-button-variants .elements-button-variant{box-shadow:none}.elements-code-preview{position:relative;display:flex;flex-direction:column;width:377px;align-items:center;justify-content:center}.elements-preview-default{border:1px solid #e1e5ee;border-radius:8px;color:#9297a1;padding:23px 10px;font-weight:400;font-size:12px;line-height:16px;text-align:center;width:60%;margin:0 auto;-webkit-user-select:none;-ms-user-select:none;user-select:none}.elements-text-preview{position:absolute;top:10px;right:10px;font-weight:600;font-size:9.5px;line-height:11px;letter-spacing:.06em;color:#fff;background-color:#535d76;border-radius:6px;padding:5px;text-transform:uppercase}}
+#page-lib-editor-tiny-plugins-elements-preview #page{padding-top:0;margin-top:0}#page-lib-editor-tiny-plugins-elements-management .col.item,#page-lib-editor-tiny-plugins-elements-printurls .col.item{min-width:250px;max-width:250px}#page-lib-editor-tiny-plugins-elements-management .col.item.addcontainer .card,#page-lib-editor-tiny-plugins-elements-printurls .col.item.addcontainer .card{background-color:#d3d3d3}#page-lib-editor-tiny-plugins-elements-management .tiny_elements_thumbnail,#page-lib-editor-tiny-plugins-elements-printurls .tiny_elements_thumbnail{width:48px;height:48px}#page-lib-editor-tiny-plugins-elements-management .preview.management,#page-lib-editor-tiny-plugins-elements-printurls .preview.management{display:flex;justify-content:space-evenly;width:100%;height:3rem;padding-bottom:.25rem}#page-lib-editor-tiny-plugins-elements-management .preview.management>div,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div{width:45%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div .elements-button-text,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div .elements-button-text{height:100%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div .elements-button-text::before,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div .elements-button-text::before{display:flex;width:unset;height:100%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more{display:flex;flex-direction:column;justify-content:center;width:10%}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more .elements-button-text::before,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more .elements-button-text::before{all:unset;content:"..."}#page-lib-editor-tiny-plugins-elements-management .preview.management>div.more .elements-button-text,#page-lib-editor-tiny-plugins-elements-printurls .preview.management>div.more .elements-button-text{word-wrap:unset}#page-lib-editor-tiny-plugins-elements-management .compcat:hover .card,#page-lib-editor-tiny-plugins-elements-printurls .compcat:hover .card{border:1px solid #787878}#page-lib-editor-tiny-plugins-elements-management .compcat.active .card,#page-lib-editor-tiny-plugins-elements-printurls .compcat.active .card{border:1px solid #0b5190}#page-lib-editor-tiny-plugins-elements-management .card-body,#page-lib-editor-tiny-plugins-elements-printurls .card-body{display:flex;flex-direction:column;padding-top:.75rem}#page-lib-editor-tiny-plugins-elements-management .card-body>div:hover,#page-lib-editor-tiny-plugins-elements-printurls .card-body>div:hover{cursor:pointer}#page-lib-editor-tiny-plugins-elements-management .card-body>div:hover+a.edit,#page-lib-editor-tiny-plugins-elements-printurls .card-body>div:hover+a.edit{color:#094478}#page-lib-editor-tiny-plugins-elements-management form[data-formtype=tiny_elements_comp_flavor] input.form-control,#page-lib-editor-tiny-plugins-elements-printurls form[data-formtype=tiny_elements_comp_flavor] input.form-control{width:100%}#page-lib-editor-tiny-plugins-elements-management .compcat[data-compcat=found-items] a,#page-lib-editor-tiny-plugins-elements-printurls .compcat[data-compcat=found-items] a{display:none}#page-lib-editor-tiny-plugins-elements-management form[data-formtype=tiny_elements_editlicense] div[data-fieldtype=static],#page-lib-editor-tiny-plugins-elements-printurls form[data-formtype=tiny_elements_editlicense] div[data-fieldtype=static]{padding-top:calc(.75rem + 1px)}.elements-plugin-container .nav.nav-tabs{flex-wrap:wrap-reverse;flex-direction:row-reverse;justify-content:flex-end}.elements-plugin-container .nav.nav-tabs li{margin-top:1px}.elements-plugin-container .nav.nav-tabs .nav-item a.active{background-color:#f6f7f8;border-bottom:1px solid #f6f7f8}.elements-plugin-container .nav.nav-tabs .nav-item a.active.dropdown-item{color:inherit}.elements-plugin-container .nav.nav-tabs,.elements-plugin-container .nav.nav-tabs .dropdown-item{font-size:.8rem}.elements-plugin-container .nav.nav-tabs .nav-link{border:1px solid #d3d3d3;border-bottom:none;border-top-right-radius:.2rem;border-top-left-radius:.2rem}.elements-buttons-preview{display:flex;justify-content:space-between;width:100%;min-height:31vh;max-height:60vh;background-color:#f6f7f8}.elements-buttons-grid{display:flex;flex-wrap:wrap;justify-content:start;row-gap:12px;column-gap:12px;margin-bottom:0;width:100%;overflow-y:auto}.elements-hidden{display:none !important;max-height:0;padding:0}.elementst-dialog-button{display:flex;flex-direction:row;align-items:center;justify-content:space-between;position:relative;height:48px;max-height:100px;width:100%;max-width:100%;background-color:#fff;border:1px solid #e4e9ec;color:#535d76;box-shadow:0 0 6px 0 rgba(0,0,0,.1);overflow-wrap:anywhere;overflow:hidden;padding:0;margin-bottom:8px}.elementst-dialog-button.elements-custom-icon .elements-button-text i{position:absolute;background-repeat:no-repeat;background-size:contain;background-position:center;min-height:23px;min-width:49px;top:auto;left:0;border-right:1px solid #dce3f0}.elementst-dialog-button .elements-button-text{display:flex;align-items:center;height:100%}.elementst-dialog-button .elements-button-text::before{position:absolute;left:0;height:100%;min-height:23px;display:flex;justify-content:center;align-items:center;border-right:1px solid #dce3f0;width:60px;height:60px;top:3px;align-content:center}.elementst-dialog-button:hover,.elementst-dialog-button:active{box-shadow:inset 0 0 3px rgba(22,121,249,.3)}.elementst-dialog-button .elements-button-text{position:relative;font-size:12px;font-weight:500;width:100%;text-align:left;padding-left:62px}.elements-code-preview{display:none;overflow:hidden;background-color:#fff;border-top:2px solid #f6f7f8}.elements-code-preview .elements-component-code{-webkit-transform:scale(0.6);-ms-transform:scale(0.6);transform:scale(0.6);width:160%;margin-top:35px;position:absolute}.elementst-dialog-button .elements-button-variants{display:grid;grid-auto-flow:column;grid-gap:3px;justify-content:end;width:100%;height:42px;margin-right:3px}.elementst-dialog-button .elements-button-variants .elements-button-variant{display:flex;justify-content:center;align-items:center;background-color:#ecf3ff;color:#1679f9;height:42px;width:36px}.elementst-dialog-button .elements-button-variants .elements-button-variant::before{content:" ";background-size:50%;background-repeat:no-repeat;background-position:center;height:42px;width:36px}.elementst-dialog-button .elements-button-variants .elements-button-variant.on,.elementst-dialog-button .elements-button-variants .elements-button-variant:hover{background-color:#1679f9;color:#fff}.elementst-dialog-button .elements-button-variants .elements-button-variant.on::before,.elementst-dialog-button .elements-button-variants .elements-button-variant:hover::before{filter:brightness(10)}body.mce-content-body .collapse:not(.show),.elements-code-preview .collapse:not(.show){display:block !important}@media only screen and (min-width: 576px){.elements-modal.modal-open .modal-dialog,.elements-modal-no-preview.modal-open .modal-dialog{min-width:550px}.elements-select-filters{display:none}.elements-buttons-filters,.elements-buttons-flavors{display:flex;justify-content:center;align-items:center;margin:10px 0 10px}}@media only screen and (min-width: 992px){.elements-modal-no-preview.modal-open .modal-dialog{max-width:550px}.elements-buttons-preview.elements-no-preview{justify-content:center}.elements-buttons-preview.elements-no-preview .elements-buttons-grid{grid-gap:9px;justify-content:center;width:405px}.elements-buttons-preview.elements-no-preview .elements-code-preview{display:none}.elements-buttons-grid{width:440px;padding:10px}.elementst-dialog-button.elements-custom-icon .elements-button-text i{top:20px;left:0;right:0;margin:0 auto;max-width:25px;min-width:25px;width:100%;border-right:none}.elementst-dialog-button{height:100px;width:116px;max-width:116px;box-shadow:none;border-radius:4px;margin-bottom:0}.elementst-dialog-button::before{content:""}.elementst-dialog-button .elements-button-text::before{left:0;right:0;margin:0 auto;border-right:none;display:inline-block;width:60px;height:60px;top:3px}.elementst-dialog-button .elements-button-text{padding:70px 8px 15px 8px;font-size:12px;font-weight:500;line-height:13px;width:80px;text-align:center;justify-content:center}.elementst-dialog-button .elements-button-variants{grid-auto-flow:row;grid-gap:0;align-content:flex-start;justify-content:start;width:36px;height:99px;background-color:#ecf3ff;margin:0}.elementst-dialog-button .elements-button-variants .elements-button-variant{display:flex;justify-content:center;align-items:center;color:#1679f9;height:33px;width:36px}.elementst-dialog-button:hover,.elementst-dialog-button:active{box-shadow:0 0 0 3px rgba(22,121,249,.3)}.elementst-dialog-button .elements-button-variants .elements-button-variant:first-child,.elementst-dialog-button .elements-button-variants .elements-button-variant:nth-child(2){border-bottom:1px solid #fff}.elementst-dialog-button:hover .elements-button-variants .elements-button-variant{box-shadow:none}.elements-code-preview{position:relative;display:flex;flex-direction:column;width:377px;align-items:center;justify-content:center}.elements-preview-default{border:1px solid #e1e5ee;border-radius:8px;color:#9297a1;padding:23px 10px;font-weight:400;font-size:12px;line-height:16px;text-align:center;width:60%;margin:0 auto;-webkit-user-select:none;-ms-user-select:none;user-select:none}.elements-text-preview{position:absolute;top:10px;right:10px;font-weight:600;font-size:9.5px;line-height:11px;letter-spacing:.06em;color:#fff;background-color:#535d76;border-radius:6px;padding:5px;text-transform:uppercase}}
diff --git a/templates/modal.mustache b/templates/modal.mustache
index 7f6d8ab..f8d124f 100644
--- a/templates/modal.mustache
+++ b/templates/modal.mustache
@@ -114,7 +114,7 @@
{{#categories}}
{{#hasFlavors}}
- -
+
-
{{name}}