1/** 2 * MMX / CATEGORY LIST 3 */ 4class MMX_CategoryList extends MMX_Element { 5 6 static get props() { 7 return { 8 'category-fallback-image': { 9 allowAny: true 10 } 11 }; 12 } 13 14 styleResourceCodes = ['mmx-base', 'mmx-category-list']; 15 #categories = []; 16 #card = {}; 17 #errorMessage = ''; 18 lifecycleReady = false; 19 20 constructor() { 21 super(); 22 this.makeComponent(); 23 this.bindRevealElement(); 24 } 25 26 // Rendering 27 render() { 28 return /*html*/` 29 <div part="wrapper"> 30 <div part="title"> 31 <slot name="title"></slot> 32 </div> 33 ${this.#renderMain()} 34 </div> 35 `; 36 } 37 38 afterRender() { 39 this.#replaceCategories(); 40 } 41 42 onDataChange() { 43 MMX.setElementAttributes(this, { 44 'data-category-fallback-image': this.data?.fallback_category_image, 45 }); 46 47 this.#card = this.data.card; 48 this.#loadCategories(); 49 } 50 51 #renderMain() { 52 if (this.#errorMessage) { 53 return this.#renderMainError(); 54 } 55 else { 56 return this.#renderCategories(); 57 } 58 } 59 60 #renderMainError() { 61 return /*html*/` 62 <div part="error" class="mmx-category-list__error"> 63 <mmx-message data-style="warning">We're unable to display the categories: ${MMX.encodeEntities(this.#errorMessage ?? 'There was a problem loading the categories')}</mmx-message> 64 </div> 65 `; 66 } 67 68 // Styles 69 styles() { 70 return this.renderResponsiveSettingStyles(); 71 } 72 73 renderResponsiveSettingStyles() { 74 let breakpointStyles = this.#renderBreakpointRule(this.data?.list?.responsive_settings?.breakpoints?.default); 75 76 MMX.getBreakpoints().map(breakpoint => { 77 const settings = this.data?.list?.responsive_settings?.breakpoints?.[breakpoint?.code] ?? {}; 78 breakpointStyles += '\n\n' + this.#renderBreakpointMedia({breakpoint, settings}); 79 }); 80 81 return breakpointStyles; 82 } 83 84 #renderBreakpointMedia({breakpoint = {}, settings = {}} = {}) { 85 if (MMX.variableType(breakpoint) !== 'object' || MMX.variableType(settings) !== 'object') { 86 return ''; 87 } 88 89 return /*css*/` 90 @media ${MMX.buildMediaQuery({breakpoint})} { 91 ${this.#renderBreakpointRule(settings)} 92 } 93 `; 94 } 95 96 #renderBreakpointRule(settings = {}) { 97 if (MMX.variableType(settings) !== 'object') { 98 return ''; 99 } 100 101 const columns = MMX.coerceNumber(settings?.columns?.value, 1); 102 const gap = MMX.composeUnitValue(settings?.gap, { 103 fallbackUnit: 'vw' 104 }) ?? '1vw'; 105 106 return /*css*/` 107 .mmx-category-list__categories { 108 --mmx-category-list__columns: ${columns}; 109 --mmx-category-list__gap: ${gap}; 110 } 111 `; 112 } 113 114 // Runtime_CategoryList_Load_Query 115 #loadCategories() { 116 this.#categories = []; 117 this.#errorMessage = ''; 118 119 MMX.Runtime_JSON_API_Call({ 120 params: this.#getListLoadQueryParams() 121 }) 122 .then(response => { 123 this.#categoriesLoaded(response); 124 }) 125 .catch(response => { 126 this.#categoriesFailedToLoad(response); 127 }); 128 } 129 130 #categoriesLoaded(response) { 131 this.#categories = response.data.data; 132 this.forceUpdate(); 133 } 134 135 #categoriesFailedToLoad(response) { 136 this.#errorMessage = response.error_message; 137 this.forceUpdate(); 138 } 139 140 #getListLoadQueryParams() { 141 const Parent_Id = MMX.coerceNumber(this.data?.list?.parent_category?.specific?.category?.id, 0); 142 143 return { 144 Function: 'Runtime_CategoryList_Load_Query', 145 Filter: this.#getFilters(), 146 Parent_Id 147 }; 148 } 149 150 #getCacheKeysFilters() { 151 return MMX.cacheKeysFilters(this.data?.list?.parent_category?.specific?.category); 152 } 153 154 #getFilters() { 155 const details = this.#getCardDetails(); 156 const filters = details.reduce((filters, detail) => { 157 const type = detail?.type?.value; 158 159 if (type === 'customfield' && !MMX.valueIsEmpty(detail?.customfield?.value)) { 160 filters.push({ 161 name: 'ondemandcolumns', 162 value: [`CustomField_Values:${detail.customfield.value}`] 163 }); 164 } 165 else if (type === 'fragment' && !MMX.valueIsEmpty(detail?.fragment?.value)) { 166 filters.push({ 167 name: 'fragments', 168 value: [detail.fragment.value] 169 }); 170 } 171 else if (type === 'image') { 172 const imageType = detail?.image_settings?.type?.value; 173 174 if (imageType === 'cattitle') { 175 filters.push({ 176 name: 'ondemandcolumns', 177 value: ['CustomField_Values:cmp-cssui-cattitle:category_title_image'] 178 }); 179 } 180 else if (imageType === 'cattree') { 181 filters.push({ 182 name: 'ondemandcolumns', 183 value: ['CustomField_Values:cmp-cssui-cattree:category_tree_image'] 184 }); 185 } 186 } 187 188 return filters; 189 }, []); 190 191 filters.push(...this.#getCacheKeysFilters()); 192 193 return filters; 194 } 195 196 // Categories 197 #renderCategories() { 198 if (MMX.arrayIsEmpty(this.#categories)) { 199 return ''; 200 } 201 202 return /*html*/` 203 <div 204 class="mmx-category-list__categories" 205 part="categories" 206 > 207 <slot name="categories"></slot> 208 </div> 209 `; 210 } 211 212 #categoriesContainer() { 213 return this.querySelector('[slot="categories"]'); 214 } 215 216 #replaceCategories() { 217 this.renderContentIntoLightDomSlot({slotName: 'categories', content: ''}); 218 this.#categoriesContainer()?.replaceChildren?.(this.#createCategories()); 219 } 220 221 #createCategories() { 222 if (!Array.isArray(this.#categories)) { 223 return ''; 224 } 225 226 const parent = new DocumentFragment();
227 const details = this.#getCardDetails(); 228 const styles = this.#card?.styles ?? {}; 229 const attributes = { 230 'data-fallback-image': this.getPropValue('category-fallback-image'), 231 'data-image-fit': this.getPropValue('category-image-fit'), 232 'data-image-type': this.getPropValue('category-image-type') 233 }; 234 235 const content = /*html*/` 236 ${this.renderThemeStylesheetTemplate(true)} 237 `; 238 239 this.#categories.forEach(category => { 240 attributes['data-category-code'] = category.code; 241 MMX_CategoryCard.create({category, details, styles, parent, attributes, content}); 242 }); 243 244 return parent; 245 } 246 247 #getCardDetails() { 248 const details = this.#card?.details?.children; 249 return Array.isArray(details) ? details : []; 250 } 251} 252 253if (!customElements.get('mmx-category-list')) { 254 customElements.define('mmx-category-list', MMX_CategoryList); 255}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.