@salla.sa/twilight-components 2.14.540 → 2.14.541

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.
Files changed (397) hide show
  1. package/dist/cjs/{filepond-U8F_iDbp.js → filepond-CNdvfaMJ.js} +1 -1
  2. package/dist/cjs/{filepond-plugin-file-poster-CAJAmCHU.js → filepond-plugin-file-poster-Di_D3w89.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-validate-size-Dxgl8Gdz.js → filepond-plugin-file-validate-size-BiRa98qd.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-type-BPhw8zXs.js → filepond-plugin-file-validate-type-Bolv-kyJ.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-image-edit-DNKfSuCy.js → filepond-plugin-image-edit-AN6huMV9.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-exif-orientation-NCmUtKOd.js → filepond-plugin-image-exif-orientation-Bwa8QtGo.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-preview-jRCd9jSG.js → filepond-plugin-image-preview-CaVYLAvG.js} +1 -1
  8. package/dist/cjs/helpers-ClOJIAG8.js +89 -0
  9. package/dist/cjs/{index-F_FURF-0.js → index-DX2EKTCE.js} +18 -14
  10. package/dist/cjs/{intlTelInput-yt9noy9U.js → intlTelInput-DnwdGAiP.js} +1 -1
  11. package/dist/cjs/loader.cjs.js +2 -2
  12. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  13. package/dist/cjs/salla-accordion_6.cjs.entry.js +1 -1
  14. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +1 -1
  15. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  16. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  17. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  19. package/dist/cjs/{salla-booking-field_7.cjs.entry.js → salla-attachment-view_9.cjs.entry.js} +1003 -40
  20. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  21. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  22. package/dist/cjs/salla-bullet-delivery_2.cjs.entry.js +1 -1
  23. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  24. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  25. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  26. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-comment-factors.cjs.entry.js +2 -2
  28. package/dist/cjs/salla-comment-form_8.cjs.entry.js +14 -5
  29. package/dist/cjs/salla-conditional-offer.cjs.entry.js +1 -1
  30. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  31. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  32. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-delivery-promise.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  36. package/dist/cjs/salla-filters-widget.cjs.entry.js +1 -1
  37. package/dist/cjs/salla-filters.cjs.entry.js +1 -1
  38. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  39. package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
  40. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  41. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +1 -1
  42. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  43. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-login-modal.cjs.entry.js +1 -1
  46. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  47. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  48. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  51. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  52. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-map_2.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-notification-item_2.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  66. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  67. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-options-modal_2.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-orders.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-products-list.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-products-slider.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  84. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +420 -84
  85. package/dist/cjs/salla-rating-modal.cjs.entry.js +3 -5
  86. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +4 -83
  87. package/dist/cjs/salla-review-order-item.cjs.entry.js +125 -0
  88. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-search.cjs.entry.js +1 -1
  90. package/dist/cjs/salla-skeleton.cjs.entry.js +1 -1
  91. package/dist/cjs/salla-slider.cjs.entry.js +1 -1
  92. package/dist/cjs/salla-social-share.cjs.entry.js +1 -1
  93. package/dist/cjs/salla-social.cjs.entry.js +1 -1
  94. package/dist/cjs/salla-tab-content_3.cjs.entry.js +1 -1
  95. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  96. package/dist/cjs/salla-tooltip.cjs.entry.js +1 -1
  97. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  98. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  100. package/dist/cjs/salla-user-settings.cjs.entry.js +1 -1
  101. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  103. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +1 -1
  104. package/dist/cjs/twilight.cjs.js +2 -2
  105. package/dist/cjs/upload-limits-C3RarAPN.js +136 -0
  106. package/dist/cjs/{vanilla-picker-DXgSpklI.js → vanilla-picker-BUFYPi-_.js} +1 -1
  107. package/dist/collection/collection-manifest.json +16 -11
  108. package/dist/collection/components/salla-attachment-view/salla-attachment-view.css +164 -0
  109. package/dist/collection/components/salla-attachment-view/salla-attachment-view.js +250 -0
  110. package/dist/collection/components/salla-attachments/salla-attachments.css +69 -0
  111. package/dist/collection/components/salla-attachments/salla-attachments.js +375 -0
  112. package/dist/collection/components/salla-comments/salla-comment-item.js +13 -3
  113. package/dist/collection/components/salla-comments/salla-comments.css +7 -0
  114. package/dist/collection/components/salla-datetime-picker/salla-datetime-picker.js +1 -1
  115. package/dist/collection/components/salla-file-upload/salla-file-upload.css +149 -0
  116. package/dist/collection/components/salla-file-upload/salla-file-upload.js +784 -42
  117. package/dist/collection/components/salla-file-upload/upload-limits.js +127 -0
  118. package/dist/collection/components/salla-products-slider/salla-products-slider.js +1 -1
  119. package/dist/collection/components/salla-rating-modal/rating-modal-media.js +85 -0
  120. package/dist/collection/components/salla-rating-modal/salla-rating-modal-v2.js +348 -85
  121. package/dist/collection/components/salla-rating-modal/salla-rating-modal.css +17 -7
  122. package/dist/collection/components/salla-rating-modal/salla-rating-modal.js +1 -3
  123. package/dist/collection/components/salla-review-factors-tags/helpers.js +8 -7
  124. package/dist/collection/components/salla-review-order-item/interfaces.js +1 -0
  125. package/dist/collection/components/salla-review-order-item/salla-review-order-item.js +154 -0
  126. package/dist/components/index.js +2 -2
  127. package/dist/components/salla-add-product-button2.js +40 -28
  128. package/dist/components/salla-attachment-view.d.ts +11 -0
  129. package/dist/components/salla-attachment-view.js +9 -0
  130. package/dist/components/salla-attachment-view2.js +323 -0
  131. package/dist/components/salla-attachments.d.ts +11 -0
  132. package/dist/components/salla-attachments.js +9 -0
  133. package/dist/components/salla-attachments2.js +232 -0
  134. package/dist/components/salla-basket-gap.js +46 -34
  135. package/dist/components/salla-breadcrumb.js +1 -1
  136. package/dist/components/salla-comment-factors2.js +1 -1
  137. package/dist/components/salla-comment-item2.js +26 -4
  138. package/dist/components/salla-comments.js +14 -2
  139. package/dist/components/salla-custom-fields.js +28 -4
  140. package/dist/components/salla-file-upload2.js +703 -31
  141. package/dist/components/salla-gifting.js +7 -1
  142. package/dist/components/salla-loyalty-exchange-product-card2.js +46 -34
  143. package/dist/components/salla-loyalty-panel.js +8 -2
  144. package/dist/components/salla-loyalty-program.js +8 -2
  145. package/dist/components/salla-multiple-bundle-product-cart2.js +40 -28
  146. package/dist/components/salla-multiple-bundle-product-details2.js +12 -6
  147. package/dist/components/salla-multiple-bundle-product-options-modal2.js +7 -1
  148. package/dist/components/salla-multiple-bundle-product-slider2.js +1 -1
  149. package/dist/components/salla-multiple-bundle-product.js +12 -6
  150. package/dist/components/salla-offer-modal.js +8 -2
  151. package/dist/components/salla-order-edit-item2.js +36 -24
  152. package/dist/components/salla-order-edit.js +42 -30
  153. package/dist/components/salla-order-options-modal2.js +34 -22
  154. package/dist/components/salla-product-card2.js +44 -32
  155. package/dist/components/salla-product-options2.js +32 -20
  156. package/dist/components/salla-products-slider2.js +8 -2
  157. package/dist/components/salla-quick-buy2.js +36 -24
  158. package/dist/components/salla-rating-modal-v22.js +449 -97
  159. package/dist/components/salla-rating-modal.js +30 -20
  160. package/dist/components/salla-review-factors-tags2.js +9 -8
  161. package/dist/components/salla-review-order-item.d.ts +11 -0
  162. package/dist/components/salla-review-order-item.js +196 -0
  163. package/dist/components/salla-reward-details2.js +8 -2
  164. package/dist/components/salla-slider2.js +1 -1
  165. package/dist/esm/{filepond-Bxzb6RRZ.js → filepond-CKsbRWmS.js} +1 -1
  166. package/dist/esm/{filepond-plugin-file-poster-CZfrjnEX.js → filepond-plugin-file-poster-B_gYCESq.js} +1 -1
  167. package/dist/esm/{filepond-plugin-file-validate-size-Dqa69lX7.js → filepond-plugin-file-validate-size-Bep6fNi3.js} +1 -1
  168. package/dist/esm/{filepond-plugin-file-validate-type-C_76nxDb.js → filepond-plugin-file-validate-type-r3uRmGIr.js} +1 -1
  169. package/dist/esm/{filepond-plugin-image-edit-BvzVMeMY.js → filepond-plugin-image-edit-BJcOlOFL.js} +1 -1
  170. package/dist/esm/{filepond-plugin-image-exif-orientation-Br4ANTi7.js → filepond-plugin-image-exif-orientation-DJoDaRwQ.js} +1 -1
  171. package/dist/esm/{filepond-plugin-image-preview-e54xZ08P.js → filepond-plugin-image-preview-BsuTLPS0.js} +1 -1
  172. package/dist/esm/helpers-DqXpn47Z.js +85 -0
  173. package/dist/esm/{index-5UtJVEzd.js → index-BVqCTDsV.js} +18 -14
  174. package/dist/esm/{intlTelInput-BQXch__5.js → intlTelInput-CD2FA0nI.js} +1 -1
  175. package/dist/esm/loader.js +3 -3
  176. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  177. package/dist/esm/salla-accordion_6.entry.js +1 -1
  178. package/dist/esm/salla-add-product-button_5.entry.js +1 -1
  179. package/dist/esm/salla-advertisement.entry.js +1 -1
  180. package/dist/esm/salla-alert_2.entry.js +1 -1
  181. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  182. package/dist/esm/salla-apps-icons.entry.js +1 -1
  183. package/dist/esm/{salla-booking-field_7.entry.js → salla-attachment-view_9.entry.js} +1002 -41
  184. package/dist/esm/salla-badge.entry.js +1 -1
  185. package/dist/esm/salla-basket-gap.entry.js +1 -1
  186. package/dist/esm/salla-bullet-delivery_2.entry.js +1 -1
  187. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  188. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  189. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  190. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  191. package/dist/esm/salla-comment-factors.entry.js +2 -2
  192. package/dist/esm/salla-comment-form_8.entry.js +14 -5
  193. package/dist/esm/salla-conditional-offer.entry.js +1 -1
  194. package/dist/esm/salla-contacts.entry.js +1 -1
  195. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  196. package/dist/esm/salla-count-down.entry.js +1 -1
  197. package/dist/esm/salla-custom-fields.entry.js +1 -1
  198. package/dist/esm/salla-delivery-promise.entry.js +1 -1
  199. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  200. package/dist/esm/salla-filters-widget.entry.js +1 -1
  201. package/dist/esm/salla-filters.entry.js +1 -1
  202. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  203. package/dist/esm/salla-gifting.entry.js +1 -1
  204. package/dist/esm/salla-hook.entry.js +1 -1
  205. package/dist/esm/salla-infinite-scroll.entry.js +1 -1
  206. package/dist/esm/salla-installment.entry.js +1 -1
  207. package/dist/esm/salla-list-tile.entry.js +1 -1
  208. package/dist/esm/salla-localization-modal.entry.js +1 -1
  209. package/dist/esm/salla-login-modal.entry.js +1 -1
  210. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  211. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  212. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  213. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  214. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  215. package/dist/esm/salla-loyalty.entry.js +1 -1
  216. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  217. package/dist/esm/salla-map_2.entry.js +1 -1
  218. package/dist/esm/salla-menu.entry.js +1 -1
  219. package/dist/esm/salla-metadata.entry.js +1 -1
  220. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  221. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  222. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  223. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  224. package/dist/esm/salla-notification-item_2.entry.js +1 -1
  225. package/dist/esm/salla-offer-modal.entry.js +1 -1
  226. package/dist/esm/salla-offer.entry.js +1 -1
  227. package/dist/esm/salla-order-branch.entry.js +1 -1
  228. package/dist/esm/salla-order-cancel.entry.js +1 -1
  229. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  230. package/dist/esm/salla-order-details-options.entry.js +1 -1
  231. package/dist/esm/salla-order-details.entry.js +1 -1
  232. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  233. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  234. package/dist/esm/salla-order-edit.entry.js +1 -1
  235. package/dist/esm/salla-order-options-modal_2.entry.js +1 -1
  236. package/dist/esm/salla-order-shipments.entry.js +1 -1
  237. package/dist/esm/salla-order-summary.entry.js +1 -1
  238. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  239. package/dist/esm/salla-orders.entry.js +1 -1
  240. package/dist/esm/salla-payments.entry.js +1 -1
  241. package/dist/esm/salla-placeholder.entry.js +1 -1
  242. package/dist/esm/salla-price-range.entry.js +1 -1
  243. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  244. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  245. package/dist/esm/salla-products-list.entry.js +1 -1
  246. package/dist/esm/salla-products-slider.entry.js +1 -1
  247. package/dist/esm/salla-quick-order.entry.js +1 -1
  248. package/dist/esm/salla-rating-modal-v2.entry.js +420 -84
  249. package/dist/esm/salla-rating-modal.entry.js +3 -5
  250. package/dist/esm/salla-review-factors-tags.entry.js +2 -81
  251. package/dist/esm/salla-review-order-item.entry.js +123 -0
  252. package/dist/esm/salla-scopes.entry.js +1 -1
  253. package/dist/esm/salla-search.entry.js +1 -1
  254. package/dist/esm/salla-skeleton.entry.js +1 -1
  255. package/dist/esm/salla-slider.entry.js +1 -1
  256. package/dist/esm/salla-social-share.entry.js +1 -1
  257. package/dist/esm/salla-social.entry.js +1 -1
  258. package/dist/esm/salla-tab-content_3.entry.js +1 -1
  259. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  260. package/dist/esm/salla-tooltip.entry.js +1 -1
  261. package/dist/esm/salla-trust-badges.entry.js +1 -1
  262. package/dist/esm/salla-user-menu.entry.js +1 -1
  263. package/dist/esm/salla-user-profile.entry.js +1 -1
  264. package/dist/esm/salla-user-settings.entry.js +1 -1
  265. package/dist/esm/salla-verify.entry.js +1 -1
  266. package/dist/esm/salla-wallet.entry.js +1 -1
  267. package/dist/esm/salla-wishlist-actions.entry.js +1 -1
  268. package/dist/esm/twilight.js +3 -3
  269. package/dist/esm/upload-limits-DrXX8XG8.js +121 -0
  270. package/dist/esm/{vanilla-picker-lLULIehu.js → vanilla-picker-Ba_za9Iy.js} +1 -1
  271. package/dist/twilight/{p-1c66fdb3.entry.js → p-003499a8.entry.js} +1 -1
  272. package/dist/twilight/{p-0bc1c246.entry.js → p-0974f6c6.entry.js} +1 -1
  273. package/dist/twilight/{p-1578cbe5.entry.js → p-0a9093f1.entry.js} +1 -1
  274. package/dist/twilight/{p-ad54610c.entry.js → p-0ce2c41f.entry.js} +1 -1
  275. package/dist/twilight/{p-acd1a9d8.entry.js → p-0ea8365b.entry.js} +1 -1
  276. package/dist/twilight/p-1174e53e.entry.js +4 -0
  277. package/dist/twilight/{p-d8a69feb.entry.js → p-11ef8b82.entry.js} +1 -1
  278. package/dist/twilight/{p-5cbbf472.entry.js → p-13244c52.entry.js} +1 -1
  279. package/dist/twilight/{p-9af31040.entry.js → p-14689252.entry.js} +1 -1
  280. package/dist/twilight/{p-397acd73.entry.js → p-17940d8a.entry.js} +1 -1
  281. package/dist/twilight/{p-40a0baa7.entry.js → p-1974f6da.entry.js} +1 -1
  282. package/dist/twilight/{p-02e821a0.entry.js → p-1a191989.entry.js} +1 -1
  283. package/dist/twilight/{p-900ee42a.entry.js → p-1ec6e0fe.entry.js} +1 -1
  284. package/dist/twilight/{p-3af52c82.entry.js → p-1fcebaad.entry.js} +1 -1
  285. package/dist/twilight/{p-6c4cef7a.entry.js → p-2b0cf190.entry.js} +1 -1
  286. package/dist/twilight/{p-7e9d67f2.entry.js → p-333ea76b.entry.js} +1 -1
  287. package/dist/twilight/{p-d6b6f984.entry.js → p-337cc9f6.entry.js} +1 -1
  288. package/dist/twilight/{p-71731e63.entry.js → p-34d5c7bf.entry.js} +1 -1
  289. package/dist/twilight/{p-49c2e935.entry.js → p-36abc50f.entry.js} +1 -1
  290. package/dist/twilight/{p-e6007706.entry.js → p-37d9f762.entry.js} +1 -1
  291. package/dist/twilight/{p-891009e2.entry.js → p-3961522a.entry.js} +1 -1
  292. package/dist/twilight/{p-b5c90151.entry.js → p-39d9e02f.entry.js} +1 -1
  293. package/dist/twilight/{p-088c1ab3.entry.js → p-3a4bb8de.entry.js} +1 -1
  294. package/dist/twilight/{p-538a1642.entry.js → p-409f95a8.entry.js} +1 -1
  295. package/dist/twilight/{p-fb2c863d.entry.js → p-42952fac.entry.js} +1 -1
  296. package/dist/twilight/{p-913d4965.entry.js → p-436dfe93.entry.js} +1 -1
  297. package/dist/twilight/{p-ed9899c0.entry.js → p-50c90e0a.entry.js} +1 -1
  298. package/dist/twilight/{p-ce941081.entry.js → p-5115117b.entry.js} +1 -1
  299. package/dist/twilight/{p-693d012f.entry.js → p-56647774.entry.js} +1 -1
  300. package/dist/twilight/{p-18d7763b.entry.js → p-598cadf4.entry.js} +1 -1
  301. package/dist/twilight/{p-66313db6.entry.js → p-635265d1.entry.js} +1 -1
  302. package/dist/twilight/{p-95224232.entry.js → p-659e2280.entry.js} +1 -1
  303. package/dist/twilight/{p-6757a647.entry.js → p-6a3487de.entry.js} +1 -1
  304. package/dist/twilight/{p-582247b3.entry.js → p-6d07d195.entry.js} +1 -1
  305. package/dist/twilight/{p-8dab94d8.entry.js → p-6ee096e8.entry.js} +1 -1
  306. package/dist/twilight/{p-c138132c.entry.js → p-723aeda4.entry.js} +1 -1
  307. package/dist/twilight/{p-c8c36233.entry.js → p-743b79ae.entry.js} +1 -1
  308. package/dist/twilight/{p-5f1b8510.entry.js → p-75e692d1.entry.js} +1 -1
  309. package/dist/twilight/{p-0895958b.entry.js → p-79444897.entry.js} +1 -1
  310. package/dist/twilight/{p-4e2e4e92.entry.js → p-7b23dec7.entry.js} +1 -1
  311. package/dist/twilight/p-7bb24ad6.entry.js +4 -0
  312. package/dist/twilight/{p-4c34e1fd.entry.js → p-7d9288b0.entry.js} +1 -1
  313. package/dist/twilight/{p-0ecbe43b.entry.js → p-85f95a58.entry.js} +1 -1
  314. package/dist/twilight/{p-2a9a4e12.entry.js → p-86a1f616.entry.js} +1 -1
  315. package/dist/twilight/{p-1bc7ab28.entry.js → p-87115499.entry.js} +1 -1
  316. package/dist/twilight/{p-497cc3a6.entry.js → p-878c2be2.entry.js} +1 -1
  317. package/dist/twilight/{p-524aabd2.entry.js → p-89e9a91f.entry.js} +1 -1
  318. package/dist/twilight/{p-f3771ebc.entry.js → p-90ee37c7.entry.js} +1 -1
  319. package/dist/twilight/{p-e4cb5ee8.entry.js → p-96584590.entry.js} +1 -1
  320. package/dist/twilight/{p-3136d942.entry.js → p-96a57e9c.entry.js} +1 -1
  321. package/dist/twilight/{p-7a17138f.entry.js → p-978e4164.entry.js} +1 -1
  322. package/dist/twilight/{p-b9b32feb.entry.js → p-97fc57c1.entry.js} +1 -1
  323. package/dist/twilight/p-98b1ff35.entry.js +4 -0
  324. package/dist/twilight/{p-5a26fb06.entry.js → p-9d18525b.entry.js} +1 -1
  325. package/dist/twilight/p-B83s3cRn.js +9 -0
  326. package/dist/twilight/{p-5UtJVEzd.js → p-BVqCTDsV.js} +1 -1
  327. package/dist/twilight/{p-5njrWPq2.js → p-CP29f7RQ.js} +2 -2
  328. package/dist/twilight/{p-CXv4fDWa.js → p-CS-t4QAX.js} +1 -1
  329. package/dist/twilight/{p-1YJa6YaH.js → p-Cluq3aDa.js} +1 -1
  330. package/dist/twilight/{p-yEKxvrPt.js → p-D-zqM6k8.js} +1 -1
  331. package/dist/twilight/{p-Dke2dnYe.js → p-DJmqmO68.js} +1 -1
  332. package/dist/twilight/{p-CkJpLPBM.js → p-DgwK96eP.js} +1 -1
  333. package/dist/twilight/p-DqXpn47Z.js +4 -0
  334. package/dist/twilight/p-DrXX8XG8.js +4 -0
  335. package/dist/twilight/{p-Cf5WZwC9.js → p-DzW3U8TR.js} +1 -1
  336. package/dist/twilight/{p-Dso9PQdO.js → p-JKh0XFIn.js} +1 -1
  337. package/dist/twilight/{p-64ee420e.entry.js → p-a347e701.entry.js} +1 -1
  338. package/dist/twilight/{p-271a2d55.entry.js → p-a5d08af6.entry.js} +1 -1
  339. package/dist/twilight/{p-fbb156e8.entry.js → p-a774f6f0.entry.js} +7 -7
  340. package/dist/twilight/p-afaa0fbe.entry.js +4 -0
  341. package/dist/twilight/p-b389b1d3.entry.js +4 -0
  342. package/dist/twilight/{p-3b0032e4.entry.js → p-b6d5ae9b.entry.js} +1 -1
  343. package/dist/twilight/{p-2d62c356.entry.js → p-b862c670.entry.js} +1 -1
  344. package/dist/twilight/{p-4e71dacc.entry.js → p-bab59319.entry.js} +1 -1
  345. package/dist/twilight/{p-16c09de9.entry.js → p-bf4c2184.entry.js} +1 -1
  346. package/dist/twilight/{p-b9477568.entry.js → p-c19cf93c.entry.js} +1 -1
  347. package/dist/twilight/{p-b16c1eac.entry.js → p-c34f8c08.entry.js} +1 -1
  348. package/dist/twilight/{p-d4e1a8f7.entry.js → p-c47ecca7.entry.js} +1 -1
  349. package/dist/twilight/{p-46d2375b.entry.js → p-c48f5c6b.entry.js} +1 -1
  350. package/dist/twilight/{p-1c049149.entry.js → p-c53c2f59.entry.js} +1 -1
  351. package/dist/twilight/{p-f5747d3c.entry.js → p-cd805a38.entry.js} +1 -1
  352. package/dist/twilight/{p-ac9a54cc.entry.js → p-cdd8ed7f.entry.js} +1 -1
  353. package/dist/twilight/{p-02df1c31.entry.js → p-ced4b7ac.entry.js} +1 -1
  354. package/dist/twilight/{p-21e9a475.entry.js → p-cf3365e2.entry.js} +1 -1
  355. package/dist/twilight/p-d78998b7.entry.js +4 -0
  356. package/dist/twilight/{p-bb7bc52f.entry.js → p-daea3207.entry.js} +1 -1
  357. package/dist/twilight/{p-6e766b2a.entry.js → p-dff6d623.entry.js} +1 -1
  358. package/dist/twilight/p-e04221de.entry.js +4 -0
  359. package/dist/twilight/{p-66448f0f.entry.js → p-e2934193.entry.js} +1 -1
  360. package/dist/twilight/{p-a9e276fa.entry.js → p-e3dab961.entry.js} +1 -1
  361. package/dist/twilight/{p-73607393.entry.js → p-e41e5fc7.entry.js} +1 -1
  362. package/dist/twilight/{p-fe222cbe.entry.js → p-e4c9cef4.entry.js} +1 -1
  363. package/dist/twilight/{p-a5afb821.entry.js → p-eaf9d536.entry.js} +1 -1
  364. package/dist/twilight/{p-c05513ae.entry.js → p-ee2b16d7.entry.js} +1 -1
  365. package/dist/twilight/{p-c224fa3a.entry.js → p-ee3ddc23.entry.js} +1 -1
  366. package/dist/twilight/{p-963d34af.entry.js → p-f0a14b18.entry.js} +1 -1
  367. package/dist/twilight/{p-4cff8533.entry.js → p-f195a63d.entry.js} +1 -1
  368. package/dist/twilight/{p-200a9c11.entry.js → p-f2c688d6.entry.js} +1 -1
  369. package/dist/twilight/{p-b7f1bad9.entry.js → p-f4ac8cae.entry.js} +1 -1
  370. package/dist/twilight/{p-1ee008c3.entry.js → p-f58d14ed.entry.js} +1 -1
  371. package/dist/twilight/{p-d505caaf.entry.js → p-f82cee0c.entry.js} +1 -1
  372. package/dist/twilight/{p-b639f062.entry.js → p-fa17989b.entry.js} +1 -1
  373. package/dist/twilight/{p-575b3b4c.entry.js → p-fbc075fe.entry.js} +1 -1
  374. package/dist/twilight/{p-f27472f4.entry.js → p-fc5d5473.entry.js} +1 -1
  375. package/dist/twilight/twilight.esm.js +1 -1
  376. package/dist/types/components/salla-attachment-view/salla-attachment-view.d.ts +47 -0
  377. package/dist/types/components/salla-attachments/salla-attachments.d.ts +73 -0
  378. package/dist/types/components/salla-comments/interfaces.d.ts +1 -0
  379. package/dist/types/components/salla-comments/salla-comment-item.d.ts +3 -1
  380. package/dist/types/components/salla-file-upload/salla-file-upload.d.ts +127 -4
  381. package/dist/types/components/salla-file-upload/upload-limits.d.ts +49 -0
  382. package/dist/types/components/salla-rating-modal/rating-modal-media.d.ts +38 -0
  383. package/dist/types/components/salla-rating-modal/salla-rating-modal-v2.d.ts +30 -7
  384. package/dist/types/components/salla-review-factors-tags/helpers.d.ts +1 -1
  385. package/dist/types/components/salla-review-factors-tags/interfaces.d.ts +7 -0
  386. package/dist/types/components/salla-review-order-item/interfaces.d.ts +30 -0
  387. package/dist/types/components/salla-review-order-item/salla-review-order-item.d.ts +30 -0
  388. package/dist/types/components.d.ts +282 -7
  389. package/package.json +5 -5
  390. package/dist/twilight/p-3024f859.entry.js +0 -4
  391. package/dist/twilight/p-3b821dc9.entry.js +0 -4
  392. package/dist/twilight/p-4dabd999.entry.js +0 -4
  393. package/dist/twilight/p-WDLGvEaR.js +0 -9
  394. package/dist/twilight/p-aaa37ca1.entry.js +0 -4
  395. package/dist/twilight/p-dcf53c45.entry.js +0 -4
  396. package/dist/twilight/p-f17fa83f.entry.js +0 -4
  397. package/dist/components/{keyboard_arrow_right.js → keyboard_arrow_left.js} +6 -6
@@ -2,6 +2,10 @@
2
2
  * Crafted with ❤ by Salla
3
3
  */
4
4
  import { h, Host } from "@stencil/core";
5
+ import { formatDuration, formatBytesAsSize, getFilePondMaxFileSize, getMaxImagesCount, getMaxVideosCount, getTotalMaxFiles, getUploadErrorMessage, isImageMimeType, isVideoMimeType, parseSizeToBytes, shouldCountGalleryFile, shouldCountGalleryLimit, usesSplitCountLimits, } from "./upload-limits";
6
+ import CancelIcon from "../../assets/svg/cancel.svg";
7
+ import PlayIcon from "../../assets/svg/play-circle.svg";
8
+ const VIDEO_METADATA_TIMEOUT_MS = 10000;
5
9
  export class SallaFileUpload {
6
10
  /**
7
11
  * Lazy load FilePond and all plugins
@@ -34,10 +38,29 @@ export class SallaFileUpload {
34
38
  constructor() {
35
39
  this.filePondLoaded = false;
36
40
  this.max_images_count = (count) => salla.lang.get('common.uploader.max_images_count', { count });
41
+ this.max_videos_count = (count) => salla.lang.get('common.uploader.max_videos_count', { count });
37
42
  this.max_count_hint = (count) => salla.lang.get('common.uploader.max_count_hint', { count });
43
+ this.video_too_large = (size) => salla.lang.get('common.uploader.video_too_large', { size });
44
+ this.video_too_long = (duration) => salla.lang.get('common.uploader.video_too_long', { duration });
38
45
  this.uploadedFiles = [];
39
46
  this.isListScrollerListenerAdded = false;
40
47
  this.showMaxCountHint = false;
48
+ this.previewFiles = [];
49
+ this.maxFilesNotified = false;
50
+ this.validationRejectedFileIds = new Set();
51
+ this.previewFilesSyncRaf = null;
52
+ /**
53
+ * Transient reservations for files currently passing add-validation, keyed by
54
+ * FilePond file id. FilePond runs beforeAddFile async and only inserts a file
55
+ * into `getFiles()` after validation resolves, so a batch of parallel picks
56
+ * would race if we counted the pond alone. We reserve a slot here synchronously
57
+ * for the in-flight window and always release it in `onaddfile` (once the file
58
+ * is committed to the pond and counted from there). Because committed files are
59
+ * always counted from `getFiles()` — never from this map — it cannot drift and
60
+ * "stick" the uploader at its limit across delete/re-upload cycles.
61
+ */
62
+ this.inFlightFiles = new Map();
63
+ this.previewObjectUrls = new Map();
41
64
  /**
42
65
  * Set the component to be profile image uploader with a preview and a circular shape
43
66
  */
@@ -66,6 +89,10 @@ export class SallaFileUpload {
66
89
  * Enable or disable file browser
67
90
  */
68
91
  this.allowBrowse = true;
92
+ /**
93
+ * Enable the custom gallery preview instead of FilePond thumbnails.
94
+ */
95
+ this.enableGallery = false;
69
96
  /**
70
97
  * Allow drop to replace a file, only works when allowMultiple is false
71
98
  */
@@ -83,7 +110,8 @@ export class SallaFileUpload {
83
110
  */
84
111
  this.allowImagePreview = true;
85
112
  /**
86
- * Fixed image preview height, overrides min and max preview height
113
+ * Fixed image preview height, overrides min and max preview height.
114
+ * Also sets the gallery thumbnail size when using the swiper preview (default: 75px).
87
115
  */
88
116
  this.imagePreviewHeight = null;
89
117
  /**
@@ -151,8 +179,9 @@ export class SallaFileUpload {
151
179
  */
152
180
  this.iconUndo = '<svg>...</svg>';
153
181
  this.openFileBrowser = () => {
154
- this.filepond.browse();
182
+ this.filepond?.browse();
155
183
  };
184
+ this.usesGalleryPreview = () => this.enableGallery;
156
185
  //if cartItemIdIsPassed, we need to set the url
157
186
  if (!this.url && this.cartItemId) {
158
187
  this.url = salla.cart.getUploadImageEndpoint();
@@ -192,13 +221,22 @@ export class SallaFileUpload {
192
221
  });
193
222
  };
194
223
  const setTranslations = async () => {
195
- await setNestedAsync('ar.trans', 'common.uploader.max_images_count', '.أضف :count صور كحد أقصى');
224
+ await setNestedAsync('ar.trans', 'common.uploader.max_images_count', 'أضف :count صور كحد أقصى');
196
225
  await setNestedAsync('en.trans', 'common.uploader.max_images_count', 'Add up to :count images maximum.');
197
226
  await setNestedAsync('ar.trans', 'common.uploader.max_count_hint', 'يمكنك رفع حتى :count صور');
198
227
  await setNestedAsync('en.trans', 'common.uploader.max_count_hint', 'You can upload up to :count images.');
228
+ await setNestedAsync('ar.trans', 'common.uploader.max_videos_count', 'أضف :count فيديوهات كحد أقصى');
229
+ await setNestedAsync('en.trans', 'common.uploader.max_videos_count', 'Add up to :count videos maximum.');
230
+ await setNestedAsync('ar.trans', 'common.uploader.video_too_large', 'حجم الفيديو يتجاوز الحد المسموح (:size)');
231
+ await setNestedAsync('en.trans', 'common.uploader.video_too_large', 'Video size exceeds the allowed limit (:size)');
232
+ await setNestedAsync('ar.trans', 'common.uploader.video_too_long', 'مدة الفيديو تتجاوز الحد المسموح (:duration)');
233
+ await setNestedAsync('en.trans', 'common.uploader.video_too_long', 'Video duration exceeds the allowed limit (:duration)');
199
234
  };
200
235
  this.max_count_hint = (count) => salla.lang.get('common.uploader.max_count_hint', { count });
201
236
  this.max_images_count = (count) => salla.lang.get('common.uploader.max_images_count', { count });
237
+ this.max_videos_count = (count) => salla.lang.get('common.uploader.max_videos_count', { count });
238
+ this.video_too_large = (size) => salla.lang.get('common.uploader.video_too_large', { size });
239
+ this.video_too_long = (duration) => salla.lang.get('common.uploader.video_too_long', { duration });
202
240
  setTranslations();
203
241
  });
204
242
  }
@@ -220,13 +258,23 @@ export class SallaFileUpload {
220
258
  }
221
259
  uploadedHandler() {
222
260
  let fileInput = this.getFormDataFileInput();
261
+ const hiddenValue = this.getHiddenInputValue();
223
262
  fileInput.type = 'hidden';
224
- fileInput.value = this.value;
225
- this.hiddenInput.value = this.value;
263
+ fileInput.value = hiddenValue;
264
+ this.hiddenInput.value = hiddenValue;
226
265
  fileInput.dispatchEvent(new window.Event('change', { bubbles: true }));
227
266
  return this.uploaded.emit(this.value);
228
267
  }
229
- removedHandler(deletedFile) {
268
+ emitRemovedFile(deletedFile) {
269
+ const removedId = deletedFile.getMetadata('id') ||
270
+ deletedFile.getMetadata('url') ||
271
+ deletedFile.serverId ||
272
+ deletedFile.id;
273
+ return this.removed.emit(removedId);
274
+ }
275
+ removedHandler(deletedFile, options = {}) {
276
+ this.releaseAcceptedFile(deletedFile.id);
277
+ this.releasePreviewObjectUrl(deletedFile.id);
230
278
  let fileInput = this.getFormDataFileInput();
231
279
  fileInput.type = 'hidden';
232
280
  fileInput.value = '';
@@ -237,10 +285,13 @@ export class SallaFileUpload {
237
285
  if (this.height) {
238
286
  setTimeout(() => this.host.querySelector('.filepond--root').style.height = this.height, 1000);
239
287
  }
240
- this.hiddenInput.value = '';
288
+ if (!this.usesGalleryPreview()) {
289
+ this.hiddenInput.value = '';
290
+ }
241
291
  fileInput.dispatchEvent(new window.Event('change', { bubbles: true }));
242
- let toDelete = this.allowMultiple ? deletedFile.getMetadata('id') : deletedFile;
243
- return this.removed.emit(toDelete);
292
+ if (!options.skipEmit) {
293
+ this.emitRemovedFile(deletedFile);
294
+ }
244
295
  }
245
296
  /**
246
297
  * Method to set option for filepond
@@ -248,6 +299,12 @@ export class SallaFileUpload {
248
299
  async setOption(key, value) {
249
300
  this.filepond[key] = value;
250
301
  }
302
+ generateUploadFileId() {
303
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
304
+ return `${this.host.id}-${crypto.randomUUID()}`;
305
+ }
306
+ return `${this.host.id}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
307
+ }
251
308
  getLabel() {
252
309
  if (this.labelIdle) {
253
310
  //some times we are passing label before translations is loaded, so here we will make sure that all translations are translated
@@ -270,6 +327,514 @@ export class SallaFileUpload {
270
327
  getFormDataFileInput() {
271
328
  return this.host.querySelector('.filepond--data input');
272
329
  }
330
+ isValidPreviewUrl(url) {
331
+ return typeof url === 'string' && (url.startsWith('http') ||
332
+ url.startsWith('blob:') ||
333
+ url.startsWith('data:') ||
334
+ url.startsWith('/'));
335
+ }
336
+ getHiddenInputValue() {
337
+ if (this.usesGalleryPreview()) {
338
+ return this.previewFiles.map(file => file.url).join(',');
339
+ }
340
+ if (typeof this.value === 'object' && this.value?.url) {
341
+ return this.value.url;
342
+ }
343
+ return this.value || '';
344
+ }
345
+ usesSplitCountLimits() {
346
+ return usesSplitCountLimits(this.maxImagesCount, this.maxVideosCount);
347
+ }
348
+ hasImageLimit() {
349
+ return this.maxImagesCount != null;
350
+ }
351
+ hasVideoLimit() {
352
+ return this.maxVideosCount != null;
353
+ }
354
+ getMaxImagesCount() {
355
+ return getMaxImagesCount(this.maxImagesCount);
356
+ }
357
+ getMaxVideosCount() {
358
+ return getMaxVideosCount(this.maxVideosCount);
359
+ }
360
+ getTotalMaxFiles() {
361
+ return getTotalMaxFiles(this.maxFilesCount, this.maxImagesCount, this.maxVideosCount);
362
+ }
363
+ getImageMaxFileSizeString() {
364
+ const storeSize = salla.config.get('store.settings.upload_size');
365
+ if (storeSize)
366
+ return `${storeSize}KB`;
367
+ return this.maxFileSize;
368
+ }
369
+ getImageMaxFileSizeBytes() {
370
+ const size = this.getImageMaxFileSizeString();
371
+ return size ? parseSizeToBytes(size) : null;
372
+ }
373
+ getVideoMaxFileSizeBytes() {
374
+ if (this.maxVideoFileSize)
375
+ return parseSizeToBytes(this.maxVideoFileSize);
376
+ return this.getImageMaxFileSizeBytes();
377
+ }
378
+ getFilePondMaxFileSizeValue() {
379
+ return getFilePondMaxFileSize(this.maxFileSize, salla.config.get('store.settings.upload_size'), this.maxVideoFileSize);
380
+ }
381
+ getFileMimeType(file) {
382
+ if (file instanceof File) {
383
+ return file.type;
384
+ }
385
+ return file.mimeType;
386
+ }
387
+ getFileUrlOrName(file) {
388
+ if (file instanceof File) {
389
+ return file.name;
390
+ }
391
+ return file.url;
392
+ }
393
+ isVideoFile(file) {
394
+ return isVideoMimeType(this.getFileMimeType(file), this.getFileUrlOrName(file));
395
+ }
396
+ getPondItemNameOrUrl(item) {
397
+ const file = item.file;
398
+ // Pre-loaded (local) files may not have a populated File object yet, so fall
399
+ // back to metadata url/name for reliable type detection.
400
+ return (file?.name ||
401
+ item.getMetadata?.('url') ||
402
+ item.getMetadata?.('name') ||
403
+ item.getMetadata?.('poster'));
404
+ }
405
+ isVideoFileFromPond(item) {
406
+ const file = item.file;
407
+ return isVideoMimeType(file?.type || item.fileType, this.getPondItemNameOrUrl(item));
408
+ }
409
+ isImageFileFromPond(item) {
410
+ const file = item.file;
411
+ return isImageMimeType(file?.type || item.fileType, this.getPondItemNameOrUrl(item));
412
+ }
413
+ getFileTypeCategory(item) {
414
+ if (this.isVideoFileFromPond(item))
415
+ return 'video';
416
+ if (this.isImageFileFromPond(item))
417
+ return 'image';
418
+ return 'other';
419
+ }
420
+ /**
421
+ * Effective count for a type = files committed to the pond + still-in-flight
422
+ * reservations that are not yet in the pond. Committed files come straight from
423
+ * `getFiles()` (the source of truth), so this self-heals after deletes and can
424
+ * never leave the uploader stuck at its limit.
425
+ */
426
+ countAcceptedByType(type, excludeId) {
427
+ const pondItems = this.getCountableGalleryItems();
428
+ const pondIds = new Set(pondItems.map(item => item.id));
429
+ let count = pondItems.filter(item => item.id !== excludeId && this.getFileTypeCategory(item) === type).length;
430
+ this.inFlightFiles.forEach((value, id) => {
431
+ if (value === type && id !== excludeId && !pondIds.has(id))
432
+ count += 1;
433
+ });
434
+ return count;
435
+ }
436
+ /** Effective total across all types (committed pond files + in-flight reservations). */
437
+ countAcceptedTotal(excludeId) {
438
+ const pondItems = this.getCountableGalleryItems().filter(item => item.id !== excludeId);
439
+ const pondIds = new Set(pondItems.map(item => item.id));
440
+ let count = pondItems.length;
441
+ this.inFlightFiles.forEach((_value, id) => {
442
+ if (id !== excludeId && !pondIds.has(id))
443
+ count += 1;
444
+ });
445
+ return count;
446
+ }
447
+ reserveAcceptedFile(id, type) {
448
+ this.inFlightFiles.set(id, type);
449
+ }
450
+ releaseAcceptedFile(id) {
451
+ this.inFlightFiles.delete(id);
452
+ }
453
+ getVideoDuration(file) {
454
+ return new Promise((resolve, reject) => {
455
+ const video = document.createElement('video');
456
+ video.preload = 'metadata';
457
+ const url = URL.createObjectURL(file);
458
+ let settled = false;
459
+ const cleanup = () => {
460
+ clearTimeout(timeoutId);
461
+ video.onloadedmetadata = null;
462
+ video.onerror = null;
463
+ video.removeAttribute('src');
464
+ video.load();
465
+ URL.revokeObjectURL(url);
466
+ };
467
+ const finish = (duration, error) => {
468
+ if (settled) {
469
+ return;
470
+ }
471
+ settled = true;
472
+ cleanup();
473
+ if (error) {
474
+ reject(error);
475
+ return;
476
+ }
477
+ resolve(duration ?? 0);
478
+ };
479
+ const timeoutId = window.setTimeout(() => {
480
+ finish(undefined, new Error('Timed out loading video metadata'));
481
+ }, VIDEO_METADATA_TIMEOUT_MS);
482
+ video.onloadedmetadata = () => {
483
+ finish(video.duration);
484
+ };
485
+ video.onerror = () => {
486
+ finish(undefined, new Error('Failed to load video metadata'));
487
+ };
488
+ video.src = url;
489
+ });
490
+ }
491
+ notifyMaxImagesLimit() {
492
+ if (this.maxFilesNotified)
493
+ return;
494
+ this.maxFilesNotified = true;
495
+ salla.notify.error(this.max_images_count(this.getMaxImagesCount()));
496
+ setTimeout(() => {
497
+ this.maxFilesNotified = false;
498
+ }, 500);
499
+ }
500
+ notifyMaxVideosLimit() {
501
+ if (this.maxFilesNotified)
502
+ return;
503
+ this.maxFilesNotified = true;
504
+ salla.notify.error(this.max_videos_count(this.getMaxVideosCount()));
505
+ setTimeout(() => {
506
+ this.maxFilesNotified = false;
507
+ }, 500);
508
+ }
509
+ notifyMaxFilesLimit() {
510
+ if (this.maxFilesNotified)
511
+ return;
512
+ this.maxFilesNotified = true;
513
+ if (this.usesSplitCountLimits() && this.hasImageLimit()) {
514
+ salla.notify.error(this.max_images_count(this.getMaxImagesCount()));
515
+ }
516
+ else {
517
+ salla.notify.error(this.max_images_count(this.maxFilesCount));
518
+ }
519
+ setTimeout(() => {
520
+ this.maxFilesNotified = false;
521
+ }, 500);
522
+ }
523
+ markValidationRejected(fileId) {
524
+ this.validationRejectedFileIds.add(fileId);
525
+ window.setTimeout(() => this.validationRejectedFileIds.delete(fileId), 1000);
526
+ }
527
+ getUploadErrorMessageForFile(error, file) {
528
+ const fileType = file ? this.getFileTypeCategory(file) : 'other';
529
+ const maxVideoBytes = this.getVideoMaxFileSizeBytes();
530
+ return getUploadErrorMessage(error, {
531
+ isVideo: fileType === 'video',
532
+ maxVideoFileSizeLabel: this.maxVideoFileSize,
533
+ maxVideoBytes: maxVideoBytes,
534
+ tooLargeLabel: salla.lang.get('common.uploader.too_large'),
535
+ invalidTypeLabel: salla.lang.get('common.uploader.invalid_type'),
536
+ genericErrorLabel: salla.lang.get('common.errors.error_occurred'),
537
+ videoTooLarge: (size) => this.video_too_large(size || formatBytesAsSize(maxVideoBytes || 0)),
538
+ });
539
+ }
540
+ notifyUploadError(error, file) {
541
+ salla.notify?.error?.(this.getUploadErrorMessageForFile(error, file));
542
+ }
543
+ removeFailedFile(file) {
544
+ if (!this.filepond?.getFiles().some(pondFile => pondFile.id === file.id)) {
545
+ return;
546
+ }
547
+ this.filepond.removeFile(file.id, { revert: false });
548
+ }
549
+ async validateBeforeAddFile(item) {
550
+ const file = item.file instanceof Blob ? item.file : undefined;
551
+ const fileType = this.getFileTypeCategory(item);
552
+ if (this.usesSplitCountLimits()) {
553
+ if (fileType === 'other') {
554
+ salla.notify.error(salla.lang.get('common.uploader.invalid_type'));
555
+ this.markValidationRejected(item.id);
556
+ return false;
557
+ }
558
+ if (fileType === 'image' && !this.hasImageLimit()) {
559
+ salla.notify.error(salla.lang.get('common.uploader.invalid_type'));
560
+ this.markValidationRejected(item.id);
561
+ return false;
562
+ }
563
+ if (fileType === 'video' && !this.hasVideoLimit()) {
564
+ salla.notify.error(salla.lang.get('common.uploader.invalid_type'));
565
+ this.markValidationRejected(item.id);
566
+ return false;
567
+ }
568
+ const maxForType = fileType === 'video' ? this.getMaxVideosCount() : this.getMaxImagesCount();
569
+ if (this.countAcceptedByType(fileType, item.id) >= maxForType) {
570
+ if (fileType === 'video') {
571
+ this.notifyMaxVideosLimit();
572
+ }
573
+ else {
574
+ this.notifyMaxImagesLimit();
575
+ }
576
+ this.markValidationRejected(item.id);
577
+ return false;
578
+ }
579
+ }
580
+ else {
581
+ if (this.countAcceptedTotal(item.id) >= this.maxFilesCount) {
582
+ this.notifyMaxFilesLimit();
583
+ this.markValidationRejected(item.id);
584
+ return false;
585
+ }
586
+ }
587
+ // Reserve the slot synchronously so parallel batch adds can't exceed the limit.
588
+ this.reserveAcceptedFile(item.id, fileType);
589
+ // Pre-loaded local files (edit mode) may not have a Blob yet; metadata is enough.
590
+ if (!file) {
591
+ return true;
592
+ }
593
+ if (fileType === 'video') {
594
+ const maxSize = this.getVideoMaxFileSizeBytes();
595
+ if (maxSize && file.size > maxSize) {
596
+ salla.notify.error(this.video_too_large(this.maxVideoFileSize || formatBytesAsSize(maxSize)));
597
+ this.releaseAcceptedFile(item.id);
598
+ this.markValidationRejected(item.id);
599
+ return false;
600
+ }
601
+ if (this.maxVideoDuration) {
602
+ try {
603
+ const duration = await this.getVideoDuration(file);
604
+ if (duration > this.maxVideoDuration) {
605
+ salla.notify.error(this.video_too_long(formatDuration(this.maxVideoDuration)));
606
+ this.releaseAcceptedFile(item.id);
607
+ this.markValidationRejected(item.id);
608
+ return false;
609
+ }
610
+ }
611
+ catch {
612
+ salla.notify.error(salla.lang.get('common.uploader.failed_to_load'));
613
+ this.releaseAcceptedFile(item.id);
614
+ this.markValidationRejected(item.id);
615
+ return false;
616
+ }
617
+ }
618
+ }
619
+ else if (fileType === 'image') {
620
+ const maxSize = this.getImageMaxFileSizeBytes();
621
+ if (maxSize && file.size > maxSize) {
622
+ salla.notify.error(salla.lang.get('common.uploader.too_large'));
623
+ this.releaseAcceptedFile(item.id);
624
+ this.markValidationRejected(item.id);
625
+ return false;
626
+ }
627
+ }
628
+ else if (this.usesSplitCountLimits()) {
629
+ salla.notify.error(salla.lang.get('common.uploader.invalid_type'));
630
+ this.releaseAcceptedFile(item.id);
631
+ this.markValidationRejected(item.id);
632
+ return false;
633
+ }
634
+ return true;
635
+ }
636
+ renderGalleryThumb(file) {
637
+ if (this.isVideoFile(file)) {
638
+ return [
639
+ h("video", { class: "s-file-upload-gallery-video-thumb", src: file.url, muted: true, playsInline: true, preload: "metadata" }),
640
+ h("span", { class: "s-file-upload-gallery-video-icon", innerHTML: PlayIcon }),
641
+ ];
642
+ }
643
+ return h("img", { src: file.url, alt: "", loading: "lazy" });
644
+ }
645
+ releasePreviewObjectUrl(fileId) {
646
+ const url = this.previewObjectUrls.get(fileId);
647
+ if (!url)
648
+ return;
649
+ URL.revokeObjectURL(url);
650
+ this.previewObjectUrls.delete(fileId);
651
+ }
652
+ prunePreviewObjectUrls(activeFileIds) {
653
+ [...this.previewObjectUrls.keys()].forEach(id => {
654
+ if (!activeFileIds.has(id)) {
655
+ this.releasePreviewObjectUrl(id);
656
+ }
657
+ });
658
+ }
659
+ revokeAllPreviewObjectUrls() {
660
+ [...this.previewObjectUrls.keys()].forEach(id => this.releasePreviewObjectUrl(id));
661
+ }
662
+ getFilePreviewUrl(item) {
663
+ const fromMeta = item.getMetadata('url') || item.getMetadata('poster');
664
+ if (this.isValidPreviewUrl(fromMeta)) {
665
+ this.releasePreviewObjectUrl(item.id);
666
+ return fromMeta;
667
+ }
668
+ const uploaded = this.uploadedFiles.find(file => file.id === item.serverId || file.id === item.getMetadata('id'));
669
+ if (uploaded?.url) {
670
+ this.releasePreviewObjectUrl(item.id);
671
+ return uploaded.url;
672
+ }
673
+ const file = item.file;
674
+ if (file instanceof Blob) {
675
+ const existing = this.previewObjectUrls.get(item.id);
676
+ if (existing)
677
+ return existing;
678
+ const url = URL.createObjectURL(file);
679
+ this.previewObjectUrls.set(item.id, url);
680
+ return url;
681
+ }
682
+ return '';
683
+ }
684
+ isFileUploading(item) {
685
+ return item.status === 3 || item.status === 7 || item.status === 9;
686
+ }
687
+ getPreviewGalleryItems() {
688
+ return (this.filepond?.getFiles() || []).filter(item => shouldCountGalleryFile(item.status));
689
+ }
690
+ getCountableGalleryItems() {
691
+ return (this.filepond?.getFiles() || []).filter(item => shouldCountGalleryLimit(item.status));
692
+ }
693
+ schedulePreviewFilesSync() {
694
+ if (!this.usesGalleryPreview())
695
+ return;
696
+ if (this.previewFilesSyncRaf != null) {
697
+ cancelAnimationFrame(this.previewFilesSyncRaf);
698
+ }
699
+ this.previewFilesSyncRaf = requestAnimationFrame(() => {
700
+ this.previewFilesSyncRaf = requestAnimationFrame(() => {
701
+ this.previewFilesSyncRaf = null;
702
+ this.syncPreviewFiles();
703
+ });
704
+ });
705
+ }
706
+ mapItemsToPreviewFiles(items) {
707
+ return items.map(item => ({
708
+ id: String(item.getMetadata('id') || item.serverId || item.id),
709
+ url: this.getFilePreviewUrl(item),
710
+ mimeType: item.file?.type || item.fileType,
711
+ processing: this.isFileUploading(item),
712
+ })).filter(file => file.url);
713
+ }
714
+ emitGalleryCounts(items) {
715
+ // Derive display counts from the settled pond items. This runs deferred
716
+ // (schedulePreviewFilesSync), so batch-rejected/over-limit files are already
717
+ // gone and the numbers always match what is actually on screen. Admission is
718
+ // guarded synchronously by the in-flight reservations in validateBeforeAddFile,
719
+ // so the pond never exceeds the configured limits — counting it here always
720
+ // reflects the real state (e.g. after deletes) instead of a stale number.
721
+ let imageCount = 0;
722
+ let videoCount = 0;
723
+ items.forEach(item => {
724
+ const type = this.getFileTypeCategory(item);
725
+ if (type === 'video')
726
+ videoCount += 1;
727
+ else if (type === 'image')
728
+ imageCount += 1;
729
+ });
730
+ let count = items.length;
731
+ if (this.hasImageLimit()) {
732
+ imageCount = Math.min(imageCount, this.getMaxImagesCount());
733
+ }
734
+ if (this.hasVideoLimit()) {
735
+ videoCount = Math.min(videoCount, this.getMaxVideosCount());
736
+ }
737
+ if (this.usesSplitCountLimits()) {
738
+ count = Math.min(count, this.getTotalMaxFiles());
739
+ }
740
+ const uploading = items.some(item => this.isFileUploading(item));
741
+ this.galleryUpdated.emit({
742
+ count,
743
+ imageCount,
744
+ videoCount,
745
+ uploading,
746
+ });
747
+ }
748
+ syncPreviewFiles() {
749
+ if (!this.usesGalleryPreview())
750
+ return;
751
+ const previewItems = this.getPreviewGalleryItems();
752
+ this.previewFiles = this.mapItemsToPreviewFiles(previewItems);
753
+ this.prunePreviewObjectUrls(new Set(previewItems.map(item => item.id)));
754
+ this.emitGalleryCounts(this.getCountableGalleryItems());
755
+ if (this.hiddenInput) {
756
+ this.hiddenInput.value = this.getHiddenInputValue();
757
+ }
758
+ }
759
+ enforceDropLabelVisibility(fileCount) {
760
+ if (!this.usesGalleryPreview())
761
+ return;
762
+ const dropLabel = this.host.querySelector('.filepond--drop-label');
763
+ if (!dropLabel)
764
+ return;
765
+ const imageCount = this.countAcceptedByType('image');
766
+ const videoCount = this.countAcceptedByType('video');
767
+ let isMaxReached = false;
768
+ if (this.usesSplitCountLimits()) {
769
+ const imagesBlocked = this.hasImageLimit() && imageCount >= this.getMaxImagesCount();
770
+ const videosBlocked = this.hasVideoLimit() && videoCount >= this.getMaxVideosCount();
771
+ if (this.hasImageLimit() && this.hasVideoLimit()) {
772
+ isMaxReached = imagesBlocked && videosBlocked;
773
+ }
774
+ else if (this.hasImageLimit()) {
775
+ isMaxReached = imagesBlocked;
776
+ }
777
+ else if (this.hasVideoLimit()) {
778
+ isMaxReached = videosBlocked;
779
+ }
780
+ }
781
+ else {
782
+ isMaxReached = (fileCount ?? this.countAcceptedTotal()) >= this.maxFilesCount;
783
+ }
784
+ this.host.classList.toggle('s-file-upload-at-max', isMaxReached);
785
+ dropLabel.classList.toggle('s-file-upload-drop-label-disabled', isMaxReached);
786
+ dropLabel.classList.remove('s-hidden');
787
+ if (isMaxReached) {
788
+ dropLabel.style.setProperty('opacity', '0.55', 'important');
789
+ dropLabel.style.setProperty('visibility', 'visible', 'important');
790
+ dropLabel.style.setProperty('transform', 'none', 'important');
791
+ dropLabel.style.setProperty('height', 'auto', 'important');
792
+ dropLabel.style.setProperty('min-height', '4.75em', 'important');
793
+ return;
794
+ }
795
+ ['opacity', 'visibility', 'transform', 'height', 'min-height'].forEach(prop => {
796
+ dropLabel.style.removeProperty(prop);
797
+ });
798
+ }
799
+ scheduleDropLabelVisibilityEnforcement(fileCount) {
800
+ this.enforceDropLabelVisibility(fileCount);
801
+ requestAnimationFrame(() => this.enforceDropLabelVisibility(fileCount));
802
+ setTimeout(() => this.enforceDropLabelVisibility(fileCount), 200);
803
+ }
804
+ removePreviewFile(event, fileId) {
805
+ event.stopPropagation();
806
+ const pondFile = this.filepond?.getFiles().find(file => String(file.getMetadata('id')) === String(fileId) || file.id === fileId);
807
+ if (pondFile) {
808
+ this.filepond.removeFile(pondFile.id);
809
+ }
810
+ }
811
+ openPreview(index) {
812
+ const file = this.previewFiles[index];
813
+ if (!file || file.processing)
814
+ return;
815
+ this.attachmentView?.open(index);
816
+ }
817
+ getGalleryThumbSize() {
818
+ return this.imagePreviewHeight || 75;
819
+ }
820
+ getGalleryStyles() {
821
+ const size = this.getGalleryThumbSize();
822
+ return {
823
+ '--s-file-upload-preview-size': `${size}px`,
824
+ '--s-file-upload-remove-size': `${Math.max(18, Math.round(size * 0.24))}px`,
825
+ '--s-file-upload-remove-icon-size': `${Math.max(10, Math.round(size * 0.13))}px`,
826
+ };
827
+ }
828
+ renderPreviewGallery() {
829
+ if (!this.usesGalleryPreview() || !this.previewFiles.length)
830
+ return null;
831
+ const galleryThumbSize = this.getGalleryThumbSize();
832
+ return (h("div", { class: "s-file-upload-gallery", style: this.getGalleryStyles() }, h("div", { class: "s-file-upload-gallery-track s-scrollbar ps-2" }, this.previewFiles.map((file, index) => (h("div", { class: "s-file-upload-gallery-item", key: file.id }, this.allowRemove && (h("button", { type: "button", class: "s-file-upload-gallery-remove", "aria-label": salla.lang.get('common.elements.remove'), onClick: (event) => this.removePreviewFile(event, file.id) }, h("span", { innerHTML: CancelIcon }))), h("div", { class: {
833
+ 's-file-upload-gallery-thumb': true,
834
+ 'is-processing': file.processing,
835
+ 'is-video': this.isVideoFile(file),
836
+ }, onClick: () => this.openPreview(index) }, this.renderGalleryThumb(file), file.processing && h("salla-loading", { size: Math.max(16, Math.round(galleryThumbSize * 0.22)) })))))), this.previewFiles.length > 0 && (h("salla-attachment-view", { ref: el => (this.attachmentView = el), attachments: this.previewFiles }))));
837
+ }
273
838
  getFiles() {
274
839
  if (!this.value && !this.files) {
275
840
  return [];
@@ -306,12 +871,20 @@ export class SallaFileUpload {
306
871
  this.host.innerHTML = '';
307
872
  }
308
873
  }
874
+ disconnectedCallback() {
875
+ if (this.previewFilesSyncRaf != null) {
876
+ cancelAnimationFrame(this.previewFilesSyncRaf);
877
+ this.previewFilesSyncRaf = null;
878
+ }
879
+ this.revokeAllPreviewObjectUrls();
880
+ }
309
881
  render() {
310
- return (h(Host, { key: '518fef1a108d79f9aeb9efdbce74e4e721a145dc', class: {
882
+ return (h(Host, { key: '9c9bb99746270f2511bd86344a135d05f9b3cf29', class: {
311
883
  "s-file-upload": true,
312
884
  "s-file-upload-profile-image": this.profileImage,
313
885
  "s-file-upload-multiple": this.allowMultiple,
314
- } }, h("input", { key: '0c01117f1e59577833b106d5825a08dd2ca51bd0', type: "file", name: this.name, value: this.value, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept }), this.allowMultiple && this.showMaxCountHint ? h("div", { class: "s-file-upload-hint s-hidden" }, this.max_count_hint(this.maxFilesCount)) : '', h("input", { key: '69276b5e95c8bc35f42c3f1f307abea8c1761621', class: "s-hidden", name: 'hidden-' + this.name, required: this.required, value: this.value, ref: input => this.hiddenInput = input })));
886
+ "s-file-upload-has-gallery": this.usesGalleryPreview(),
887
+ } }, h("input", { key: 'd033124b1829ee4b9f0c4d44041a525e73769b77', type: "file", name: this.name, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept }), this.allowMultiple && this.showMaxCountHint ? h("div", { class: "s-file-upload-hint s-hidden" }, this.max_count_hint(this.getTotalMaxFiles())) : '', h("input", { key: '95608637f6b52dabbad4a7374d329549cb6d3862', class: "s-hidden", name: 'hidden-' + this.name, required: this.required, value: this.getHiddenInputValue(), ref: input => this.hiddenInput = input }), this.renderPreviewGallery()));
315
888
  }
316
889
  async componentDidLoad() {
317
890
  await Salla.onReady();
@@ -320,6 +893,7 @@ export class SallaFileUpload {
320
893
  this.value = Salla.config.get('user.avatar');
321
894
  }
322
895
  let files = this.getFiles();
896
+ const filePondMaxFileSize = this.getFilePondMaxFileSizeValue();
323
897
  this.filepond = this.FilePond.create(this.fileUploader, {
324
898
  name: this.payloadName || this.name,
325
899
  //@ts-ignore
@@ -336,10 +910,13 @@ export class SallaFileUpload {
336
910
  allowReorder: this.allowReorder,
337
911
  storeAsFile: this.storeAsFile,
338
912
  forceRevert: this.forceRevert,
339
- maxFiles: this.maxFilesCount,
913
+ // FilePond hides the drop label (opacity 0 + height 0) when maxFiles is reached.
914
+ // In gallery mode we enforce the limit ourselves so labels stay visible, dimmed at max.
915
+ maxFiles: this.usesGalleryPreview() ? null : this.getTotalMaxFiles(),
916
+ beforeAddFile: (item) => this.validateBeforeAddFile(item),
340
917
  imagePreviewHeight: this.imagePreviewHeight,
341
918
  filePosterHeight: this.filePosterHeight,
342
- allowImagePreview: this.allowImagePreview,
919
+ allowImagePreview: this.usesGalleryPreview() ? false : this.allowImagePreview,
343
920
  maxParallelUploads: this.maxParallelUploads,
344
921
  checkValidity: this.checkValidity,
345
922
  itemInsertLocation: this.itemInsertLocation,
@@ -349,36 +926,80 @@ export class SallaFileUpload {
349
926
  dropOnElement: this.dropOnElement,
350
927
  dropValidation: this.dropValidation,
351
928
  ignoredFiles: this.ignoredFiles,
352
- onaddfile: (error, file) => this.addedHandler(error, file),
929
+ onaddfile: (error, file) => {
930
+ // The file is now committed to the pond (or errored out), so it is
931
+ // counted from getFiles() from here on — drop the in-flight reservation
932
+ // either way to keep the map transient and drift-free.
933
+ this.releaseAcceptedFile(file.id);
934
+ if (error) {
935
+ if (!this.validationRejectedFileIds.has(file.id)) {
936
+ this.notifyUploadError(error, file);
937
+ }
938
+ this.validationRejectedFileIds.delete(file.id);
939
+ }
940
+ this.addedHandler(error, file);
941
+ if (this.usesGalleryPreview()) {
942
+ this.schedulePreviewFilesSync();
943
+ }
944
+ },
353
945
  onwarning: (error, _file, _status) => {
354
946
  if (error.body === "Max files") {
355
- salla.notify.error(this.max_images_count(this.maxFilesCount));
947
+ this.notifyMaxFilesLimit();
356
948
  }
357
949
  },
358
950
  onprocessfile: (error, file) => {
951
+ if (error) {
952
+ this.notifyUploadError(error, file);
953
+ this.removeFailedFile(file);
954
+ if (this.usesGalleryPreview()) {
955
+ this.schedulePreviewFilesSync();
956
+ }
957
+ return;
958
+ }
959
+ let syncedGallery = false;
359
960
  if (this.allowMultiple && !error) {
360
961
  // Find the uploaded file data using the serverId returned from onload
361
962
  const serverId = file.serverId;
362
963
  const uploadedFile = this.uploadedFiles.find(f => f.id === serverId);
363
964
  if (uploadedFile) {
364
- // Set metadata on the file
365
965
  file.setMetadata('id', uploadedFile.id, true);
366
966
  file.setMetadata('url', uploadedFile.url, true);
367
967
  this.value = {
368
968
  id: uploadedFile.id,
369
969
  url: uploadedFile.url
370
970
  };
971
+ if (this.usesGalleryPreview()) {
972
+ this.syncPreviewFiles();
973
+ syncedGallery = true;
974
+ }
371
975
  this.uploadedHandler();
372
976
  }
373
977
  }
978
+ if (this.usesGalleryPreview() && !syncedGallery) {
979
+ this.schedulePreviewFilesSync();
980
+ }
981
+ },
982
+ onremovefile: (_error, file) => {
983
+ if (this.usesGalleryPreview()) {
984
+ if (file) {
985
+ this.emitRemovedFile(file);
986
+ }
987
+ this.schedulePreviewFilesSync();
988
+ }
374
989
  },
375
990
  onupdatefiles: (fileItems) => {
376
991
  const dropLabel = this.host.querySelector('.filepond--drop-label');
377
992
  const listScroller = this.host.querySelector('.filepond--list-scroller');
993
+ if (this.usesGalleryPreview()) {
994
+ listScroller?.classList.add('s-hidden');
995
+ this.scheduleDropLabelVisibilityEnforcement();
996
+ this.schedulePreviewFilesSync();
997
+ return;
998
+ }
378
999
  if (this.allowMultiple) {
379
1000
  if (fileItems.length > 0) {
380
1001
  dropLabel.classList.add('s-hidden');
381
- if (!this.isListScrollerListenerAdded && fileItems.length < this.maxFilesCount) {
1002
+ if (!this.isListScrollerListenerAdded && fileItems.length < this.getTotalMaxFiles()) {
382
1003
  this.showMaxCountHint = true;
383
1004
  listScroller.addEventListener('click', this.openFileBrowser);
384
1005
  this.isListScrollerListenerAdded = true;
@@ -391,8 +1012,7 @@ export class SallaFileUpload {
391
1012
  this.isListScrollerListenerAdded = false;
392
1013
  }
393
1014
  }
394
- // Check if fileItems length equals maxFilesCount and remove event listener
395
- if (fileItems.length === this.maxFilesCount && this.isListScrollerListenerAdded) {
1015
+ if (fileItems.length === this.getTotalMaxFiles() && this.isListScrollerListenerAdded) {
396
1016
  listScroller.removeEventListener('click', this.openFileBrowser);
397
1017
  this.isListScrollerListenerAdded = false;
398
1018
  this.showMaxCountHint = false;
@@ -408,21 +1028,28 @@ export class SallaFileUpload {
408
1028
  onload: (response) => {
409
1029
  let responseData = JSON.parse(response).data;
410
1030
  if ((this.instantUpload || this.allowMultiple) && Array.isArray(responseData) && responseData.length > 0) {
411
- const fileId = `${this.host.id}-${Math.floor(100 + Math.random() * 900)}`;
412
- const fileUrl = responseData[0];
1031
+ const item = responseData[0];
1032
+ const fileUrl = typeof item === 'object' && item !== null ? item.filePath || item.url : item;
1033
+ const fileId = this.generateUploadFileId();
413
1034
  // Store the upload result to be picked up by onprocessfile
414
- this.uploadedFiles.push({
415
- id: fileId,
416
- url: fileUrl
417
- });
1035
+ this.uploadedFiles = [
1036
+ ...this.uploadedFiles,
1037
+ { id: fileId, url: fileUrl }
1038
+ ];
418
1039
  // Return the serverId that FilePond will use to identify this file
419
1040
  // We'll use the fileId as the serverId
420
1041
  return fileId;
421
1042
  }
422
1043
  else {
423
- this.value = responseData.filePath || responseData.url;
1044
+ const fileUrl = typeof responseData === 'object' && responseData !== null
1045
+ ? responseData.filePath || responseData.url
1046
+ : responseData;
1047
+ const fileId = typeof responseData === 'object' && responseData !== null && responseData.id
1048
+ ? responseData.id
1049
+ : this.generateUploadFileId();
1050
+ this.value = { id: fileId, url: fileUrl };
424
1051
  this.uploadedHandler();
425
- return this.value;
1052
+ return fileUrl;
426
1053
  }
427
1054
  },
428
1055
  headers: salla.api.getHeaders(),
@@ -487,13 +1114,19 @@ export class SallaFileUpload {
487
1114
  fileValidateTypeLabelExpectedTypes: '{allButLastType}, {lastType}',
488
1115
  labelMaxFileSizeExceeded: salla.lang.get('common.uploader.too_large'),
489
1116
  labelMaxFileSize: '{filesize}',
490
- maxFileSize: `${salla.config.get('store.settings.upload_size')}KB` || this.maxFileSize,
1117
+ ...(filePondMaxFileSize ? { maxFileSize: filePondMaxFileSize } : {}),
491
1118
  beforeRemoveFile: (deletedFile) => new Promise((resolve) => {
492
- this.removedHandler(deletedFile);
1119
+ this.removedHandler(deletedFile, { skipEmit: this.usesGalleryPreview() });
493
1120
  resolve(true);
494
1121
  }),
495
1122
  });
496
- ``;
1123
+ this.syncPreviewFiles();
1124
+ if (this.usesGalleryPreview()) {
1125
+ this.filepond.on('updatefiles', () => {
1126
+ this.scheduleDropLabelVisibilityEnforcement();
1127
+ });
1128
+ this.scheduleDropLabelVisibilityEnforcement();
1129
+ }
497
1130
  this.hiddenInput.addEventListener('invalid', e => {
498
1131
  this.invalidInput.emit(e);
499
1132
  });
@@ -925,6 +1558,26 @@ export class SallaFileUpload {
925
1558
  "setter": false,
926
1559
  "reflect": false
927
1560
  },
1561
+ "enableGallery": {
1562
+ "type": "boolean",
1563
+ "attribute": "enable-gallery",
1564
+ "mutable": false,
1565
+ "complexType": {
1566
+ "original": "boolean",
1567
+ "resolved": "boolean",
1568
+ "references": {}
1569
+ },
1570
+ "required": false,
1571
+ "optional": false,
1572
+ "docs": {
1573
+ "tags": [],
1574
+ "text": "Enable the custom gallery preview instead of FilePond thumbnails."
1575
+ },
1576
+ "getter": false,
1577
+ "setter": false,
1578
+ "reflect": true,
1579
+ "defaultValue": "false"
1580
+ },
928
1581
  "allowReplace": {
929
1582
  "type": "boolean",
930
1583
  "attribute": "allow-replace",
@@ -1056,7 +1709,7 @@ export class SallaFileUpload {
1056
1709
  "optional": false,
1057
1710
  "docs": {
1058
1711
  "tags": [],
1059
- "text": "Fixed image preview height, overrides min and max preview height"
1712
+ "text": "Fixed image preview height, overrides min and max preview height.\nAlso sets the gallery thumbnail size when using the swiper preview (default: 75px)."
1060
1713
  },
1061
1714
  "getter": false,
1062
1715
  "setter": false,
@@ -1161,6 +1814,82 @@ export class SallaFileUpload {
1161
1814
  "reflect": false,
1162
1815
  "defaultValue": "3"
1163
1816
  },
1817
+ "maxImagesCount": {
1818
+ "type": "number",
1819
+ "attribute": "max-images-count",
1820
+ "mutable": false,
1821
+ "complexType": {
1822
+ "original": "number",
1823
+ "resolved": "number",
1824
+ "references": {}
1825
+ },
1826
+ "required": false,
1827
+ "optional": false,
1828
+ "docs": {
1829
+ "tags": [],
1830
+ "text": "Primary: maximum number of image files (split count mode).\nWhen set with or without maxVideosCount, maxFilesCount is ignored for count limits."
1831
+ },
1832
+ "getter": false,
1833
+ "setter": false,
1834
+ "reflect": false
1835
+ },
1836
+ "maxVideosCount": {
1837
+ "type": "number",
1838
+ "attribute": "max-videos-count",
1839
+ "mutable": false,
1840
+ "complexType": {
1841
+ "original": "number",
1842
+ "resolved": "number",
1843
+ "references": {}
1844
+ },
1845
+ "required": false,
1846
+ "optional": false,
1847
+ "docs": {
1848
+ "tags": [],
1849
+ "text": "Primary: maximum number of video files (split count mode).\nWhen set with or without maxImagesCount, maxFilesCount is ignored for count limits."
1850
+ },
1851
+ "getter": false,
1852
+ "setter": false,
1853
+ "reflect": false
1854
+ },
1855
+ "maxVideoFileSize": {
1856
+ "type": "string",
1857
+ "attribute": "max-video-file-size",
1858
+ "mutable": false,
1859
+ "complexType": {
1860
+ "original": "`${number}MB` | `${number}KB`",
1861
+ "resolved": "`${number}KB` | `${number}MB`",
1862
+ "references": {}
1863
+ },
1864
+ "required": false,
1865
+ "optional": false,
1866
+ "docs": {
1867
+ "tags": [],
1868
+ "text": "Maximum size for video files only, e.g. 50MB or 750KB"
1869
+ },
1870
+ "getter": false,
1871
+ "setter": false,
1872
+ "reflect": false
1873
+ },
1874
+ "maxVideoDuration": {
1875
+ "type": "number",
1876
+ "attribute": "max-video-duration",
1877
+ "mutable": false,
1878
+ "complexType": {
1879
+ "original": "number",
1880
+ "resolved": "number",
1881
+ "references": {}
1882
+ },
1883
+ "required": false,
1884
+ "optional": false,
1885
+ "docs": {
1886
+ "tags": [],
1887
+ "text": "Maximum video duration in seconds"
1888
+ },
1889
+ "getter": false,
1890
+ "setter": false,
1891
+ "reflect": false
1892
+ },
1164
1893
  "checkValidity": {
1165
1894
  "type": "boolean",
1166
1895
  "attribute": "check-validity",
@@ -1575,10 +2304,14 @@ export class SallaFileUpload {
1575
2304
  static get states() {
1576
2305
  return {
1577
2306
  "max_images_count": {},
2307
+ "max_videos_count": {},
1578
2308
  "max_count_hint": {},
2309
+ "video_too_large": {},
2310
+ "video_too_long": {},
1579
2311
  "uploadedFiles": {},
1580
2312
  "isListScrollerListenerAdded": {},
1581
- "showMaxCountHint": {}
2313
+ "showMaxCountHint": {},
2314
+ "previewFiles": {}
1582
2315
  };
1583
2316
  }
1584
2317
  static get events() {
@@ -1646,18 +2379,27 @@ export class SallaFileUpload {
1646
2379
  "composed": true,
1647
2380
  "docs": {
1648
2381
  "tags": [],
1649
- "text": "Event emitted when the file is about to be removed. Returns boolean value."
2382
+ "text": "Event emitted when the file is about to be removed. Returns the removed file id when available."
1650
2383
  },
1651
2384
  "complexType": {
1652
- "original": "FilePondFile",
1653
- "resolved": "FilePondFile",
1654
- "references": {
1655
- "FilePondFile": {
1656
- "location": "import",
1657
- "path": "./interfaces",
1658
- "id": "src/components/salla-file-upload/interfaces.ts::FilePondFile"
1659
- }
1660
- }
2385
+ "original": "string | number",
2386
+ "resolved": "number | string",
2387
+ "references": {}
2388
+ }
2389
+ }, {
2390
+ "method": "galleryUpdated",
2391
+ "name": "galleryUpdated",
2392
+ "bubbles": true,
2393
+ "cancelable": true,
2394
+ "composed": true,
2395
+ "docs": {
2396
+ "tags": [],
2397
+ "text": "Event emitted when the gallery preview list changes (upload/remove)."
2398
+ },
2399
+ "complexType": {
2400
+ "original": "{\n count: number;\n imageCount?: number;\n videoCount?: number;\n uploading?: boolean;\n }",
2401
+ "resolved": "{ count: number; imageCount?: number; videoCount?: number; uploading?: boolean; }",
2402
+ "references": {}
1661
2403
  }
1662
2404
  }];
1663
2405
  }