satis 1.0.75 → 2.0.7

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 (671) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +47 -25
  3. data/app/assets/config/satis_manifest.js +8 -1
  4. data/app/assets/fontawesome/brands.js +6 -0
  5. data/app/assets/fontawesome/fontawesome.js +6 -0
  6. data/app/assets/fontawesome/solid.js +6 -0
  7. data/app/assets/images/satis/flags/1x1/ac.svg +81 -0
  8. data/app/assets/images/satis/flags/1x1/ad.svg +148 -0
  9. data/app/assets/images/satis/flags/1x1/ae.svg +6 -0
  10. data/app/assets/images/satis/flags/1x1/af.svg +81 -0
  11. data/app/assets/images/satis/flags/1x1/ag.svg +14 -0
  12. data/app/assets/images/satis/flags/1x1/ai.svg +763 -0
  13. data/app/assets/images/satis/flags/1x1/al.svg +5 -0
  14. data/app/assets/images/satis/flags/1x1/am.svg +5 -0
  15. data/app/assets/images/satis/flags/1x1/ao.svg +13 -0
  16. data/app/assets/images/satis/flags/1x1/aq.svg +5 -0
  17. data/app/assets/images/satis/flags/1x1/ar.svg +31 -0
  18. data/app/assets/images/satis/flags/1x1/as.svg +33 -0
  19. data/app/assets/images/satis/flags/1x1/at.svg +6 -0
  20. data/app/assets/images/satis/flags/1x1/au.svg +11 -0
  21. data/app/assets/images/satis/flags/1x1/aw.svg +186 -0
  22. data/app/assets/images/satis/flags/1x1/ax.svg +18 -0
  23. data/app/assets/images/satis/flags/1x1/az.svg +8 -0
  24. data/app/assets/images/satis/flags/1x1/ba.svg +12 -0
  25. data/app/assets/images/satis/flags/1x1/bb.svg +6 -0
  26. data/app/assets/images/satis/flags/1x1/bd.svg +4 -0
  27. data/app/assets/images/satis/flags/1x1/be.svg +7 -0
  28. data/app/assets/images/satis/flags/1x1/bf.svg +7 -0
  29. data/app/assets/images/satis/flags/1x1/bg.svg +7 -0
  30. data/app/assets/images/satis/flags/1x1/bh.svg +9 -0
  31. data/app/assets/images/satis/flags/1x1/bi.svg +15 -0
  32. data/app/assets/images/satis/flags/1x1/bj.svg +14 -0
  33. data/app/assets/images/satis/flags/1x1/bl.svg +7 -0
  34. data/app/assets/images/satis/flags/1x1/bm.svg +99 -0
  35. data/app/assets/images/satis/flags/1x1/bn.svg +36 -0
  36. data/app/assets/images/satis/flags/1x1/bo.svg +678 -0
  37. data/app/assets/images/satis/flags/1x1/bq.svg +5 -0
  38. data/app/assets/images/satis/flags/1x1/br.svg +45 -0
  39. data/app/assets/images/satis/flags/1x1/bs.svg +13 -0
  40. data/app/assets/images/satis/flags/1x1/bt.svg +89 -0
  41. data/app/assets/images/satis/flags/1x1/bv.svg +13 -0
  42. data/app/assets/images/satis/flags/1x1/bw.svg +7 -0
  43. data/app/assets/images/satis/flags/1x1/by.svg +22 -0
  44. data/app/assets/images/satis/flags/1x1/bz.svg +145 -0
  45. data/app/assets/images/satis/flags/1x1/ca.svg +4 -0
  46. data/app/assets/images/satis/flags/1x1/cc.svg +19 -0
  47. data/app/assets/images/satis/flags/1x1/cd.svg +12 -0
  48. data/app/assets/images/satis/flags/1x1/cf.svg +15 -0
  49. data/app/assets/images/satis/flags/1x1/cg.svg +12 -0
  50. data/app/assets/images/satis/flags/1x1/ch.svg +9 -0
  51. data/app/assets/images/satis/flags/1x1/ci.svg +7 -0
  52. data/app/assets/images/satis/flags/1x1/ck.svg +11 -0
  53. data/app/assets/images/satis/flags/1x1/cl.svg +13 -0
  54. data/app/assets/images/satis/flags/1x1/cm.svg +15 -0
  55. data/app/assets/images/satis/flags/1x1/cn.svg +11 -0
  56. data/app/assets/images/satis/flags/1x1/co.svg +7 -0
  57. data/app/assets/images/satis/flags/1x1/cp.svg +7 -0
  58. data/app/assets/images/satis/flags/1x1/cr.svg +7 -0
  59. data/app/assets/images/satis/flags/1x1/cu.svg +13 -0
  60. data/app/assets/images/satis/flags/1x1/cv.svg +13 -0
  61. data/app/assets/images/satis/flags/1x1/cw.svg +14 -0
  62. data/app/assets/images/satis/flags/1x1/cx.svg +15 -0
  63. data/app/assets/images/satis/flags/1x1/cy.svg +6 -0
  64. data/app/assets/images/satis/flags/1x1/cz.svg +5 -0
  65. data/app/assets/images/satis/flags/1x1/de.svg +5 -0
  66. data/app/assets/images/satis/flags/1x1/dg.svg +140 -0
  67. data/app/assets/images/satis/flags/1x1/dj.svg +13 -0
  68. data/app/assets/images/satis/flags/1x1/dk.svg +5 -0
  69. data/app/assets/images/satis/flags/1x1/dm.svg +152 -0
  70. data/app/assets/images/satis/flags/1x1/do.svg +6745 -0
  71. data/app/assets/images/satis/flags/1x1/dz.svg +5 -0
  72. data/app/assets/images/satis/flags/1x1/ea.svg +547 -0
  73. data/app/assets/images/satis/flags/1x1/ec.svg +138 -0
  74. data/app/assets/images/satis/flags/1x1/ee.svg +7 -0
  75. data/app/assets/images/satis/flags/1x1/eg.svg +38 -0
  76. data/app/assets/images/satis/flags/1x1/eh.svg +16 -0
  77. data/app/assets/images/satis/flags/1x1/er.svg +13 -0
  78. data/app/assets/images/satis/flags/1x1/es-ct.svg +4 -0
  79. data/app/assets/images/satis/flags/1x1/es-ga.svg +189 -0
  80. data/app/assets/images/satis/flags/1x1/es.svg +547 -0
  81. data/app/assets/images/satis/flags/1x1/et.svg +14 -0
  82. data/app/assets/images/satis/flags/1x1/eu.svg +28 -0
  83. data/app/assets/images/satis/flags/1x1/fi.svg +5 -0
  84. data/app/assets/images/satis/flags/1x1/fj.svg +125 -0
  85. data/app/assets/images/satis/flags/1x1/fk.svg +93 -0
  86. data/app/assets/images/satis/flags/1x1/fm.svg +11 -0
  87. data/app/assets/images/satis/flags/1x1/fo.svg +12 -0
  88. data/app/assets/images/satis/flags/1x1/fr.svg +7 -0
  89. data/app/assets/images/satis/flags/1x1/ga.svg +7 -0
  90. data/app/assets/images/satis/flags/1x1/gb-eng.svg +5 -0
  91. data/app/assets/images/satis/flags/1x1/gb-nir.svg +131 -0
  92. data/app/assets/images/satis/flags/1x1/gb-sct.svg +4 -0
  93. data/app/assets/images/satis/flags/1x1/gb-wls.svg +9 -0
  94. data/app/assets/images/satis/flags/1x1/gb.svg +7 -0
  95. data/app/assets/images/satis/flags/1x1/gd.svg +27 -0
  96. data/app/assets/images/satis/flags/1x1/ge.svg +6 -0
  97. data/app/assets/images/satis/flags/1x1/gf.svg +7 -0
  98. data/app/assets/images/satis/flags/1x1/gg.svg +9 -0
  99. data/app/assets/images/satis/flags/1x1/gh.svg +6 -0
  100. data/app/assets/images/satis/flags/1x1/gi.svg +32 -0
  101. data/app/assets/images/satis/flags/1x1/gl.svg +4 -0
  102. data/app/assets/images/satis/flags/1x1/gm.svg +9 -0
  103. data/app/assets/images/satis/flags/1x1/gn.svg +7 -0
  104. data/app/assets/images/satis/flags/1x1/gp.svg +7 -0
  105. data/app/assets/images/satis/flags/1x1/gq.svg +23 -0
  106. data/app/assets/images/satis/flags/1x1/gr.svg +16 -0
  107. data/app/assets/images/satis/flags/1x1/gs.svg +245 -0
  108. data/app/assets/images/satis/flags/1x1/gt.svg +204 -0
  109. data/app/assets/images/satis/flags/1x1/gu.svg +39 -0
  110. data/app/assets/images/satis/flags/1x1/gw.svg +15 -0
  111. data/app/assets/images/satis/flags/1x1/gy.svg +9 -0
  112. data/app/assets/images/satis/flags/1x1/hk.svg +30 -0
  113. data/app/assets/images/satis/flags/1x1/hm.svg +11 -0
  114. data/app/assets/images/satis/flags/1x1/hn.svg +18 -0
  115. data/app/assets/images/satis/flags/1x1/hr.svg +56 -0
  116. data/app/assets/images/satis/flags/1x1/ht.svg +116 -0
  117. data/app/assets/images/satis/flags/1x1/hu.svg +7 -0
  118. data/app/assets/images/satis/flags/1x1/ic.svg +7 -0
  119. data/app/assets/images/satis/flags/1x1/id.svg +6 -0
  120. data/app/assets/images/satis/flags/1x1/ie.svg +7 -0
  121. data/app/assets/images/satis/flags/1x1/il.svg +14 -0
  122. data/app/assets/images/satis/flags/1x1/im.svg +36 -0
  123. data/app/assets/images/satis/flags/1x1/in.svg +25 -0
  124. data/app/assets/images/satis/flags/1x1/io.svg +140 -0
  125. data/app/assets/images/satis/flags/1x1/iq.svg +10 -0
  126. data/app/assets/images/satis/flags/1x1/ir.svg +219 -0
  127. data/app/assets/images/satis/flags/1x1/is.svg +12 -0
  128. data/app/assets/images/satis/flags/1x1/it.svg +7 -0
  129. data/app/assets/images/satis/flags/1x1/je.svg +47 -0
  130. data/app/assets/images/satis/flags/1x1/jm.svg +8 -0
  131. data/app/assets/images/satis/flags/1x1/jo.svg +16 -0
  132. data/app/assets/images/satis/flags/1x1/jp.svg +11 -0
  133. data/app/assets/images/satis/flags/1x1/ke.svg +23 -0
  134. data/app/assets/images/satis/flags/1x1/kg.svg +15 -0
  135. data/app/assets/images/satis/flags/1x1/kh.svg +61 -0
  136. data/app/assets/images/satis/flags/1x1/ki.svg +36 -0
  137. data/app/assets/images/satis/flags/1x1/km.svg +16 -0
  138. data/app/assets/images/satis/flags/1x1/kn.svg +14 -0
  139. data/app/assets/images/satis/flags/1x1/kp.svg +15 -0
  140. data/app/assets/images/satis/flags/1x1/kr.svg +24 -0
  141. data/app/assets/images/satis/flags/1x1/kw.svg +13 -0
  142. data/app/assets/images/satis/flags/1x1/ky.svg +225 -0
  143. data/app/assets/images/satis/flags/1x1/kz.svg +23 -0
  144. data/app/assets/images/satis/flags/1x1/la.svg +12 -0
  145. data/app/assets/images/satis/flags/1x1/lb.svg +15 -0
  146. data/app/assets/images/satis/flags/1x1/lc.svg +8 -0
  147. data/app/assets/images/satis/flags/1x1/li.svg +43 -0
  148. data/app/assets/images/satis/flags/1x1/lk.svg +22 -0
  149. data/app/assets/images/satis/flags/1x1/lr.svg +14 -0
  150. data/app/assets/images/satis/flags/1x1/ls.svg +8 -0
  151. data/app/assets/images/satis/flags/1x1/lt.svg +7 -0
  152. data/app/assets/images/satis/flags/1x1/lu.svg +5 -0
  153. data/app/assets/images/satis/flags/1x1/lv.svg +6 -0
  154. data/app/assets/images/satis/flags/1x1/ly.svg +13 -0
  155. data/app/assets/images/satis/flags/1x1/ma.svg +4 -0
  156. data/app/assets/images/satis/flags/1x1/mc.svg +6 -0
  157. data/app/assets/images/satis/flags/1x1/md.svg +71 -0
  158. data/app/assets/images/satis/flags/1x1/me.svg +118 -0
  159. data/app/assets/images/satis/flags/1x1/mf.svg +7 -0
  160. data/app/assets/images/satis/flags/1x1/mg.svg +7 -0
  161. data/app/assets/images/satis/flags/1x1/mh.svg +8 -0
  162. data/app/assets/images/satis/flags/1x1/mk.svg +5 -0
  163. data/app/assets/images/satis/flags/1x1/ml.svg +7 -0
  164. data/app/assets/images/satis/flags/1x1/mm.svg +16 -0
  165. data/app/assets/images/satis/flags/1x1/mn.svg +13 -0
  166. data/app/assets/images/satis/flags/1x1/mo.svg +9 -0
  167. data/app/assets/images/satis/flags/1x1/mp.svg +86 -0
  168. data/app/assets/images/satis/flags/1x1/mq.svg +7 -0
  169. data/app/assets/images/satis/flags/1x1/mr.svg +6 -0
  170. data/app/assets/images/satis/flags/1x1/ms.svg +70 -0
  171. data/app/assets/images/satis/flags/1x1/mt.svg +50 -0
  172. data/app/assets/images/satis/flags/1x1/mu.svg +8 -0
  173. data/app/assets/images/satis/flags/1x1/mv.svg +6 -0
  174. data/app/assets/images/satis/flags/1x1/mw.svg +15 -0
  175. data/app/assets/images/satis/flags/1x1/mx.svg +378 -0
  176. data/app/assets/images/satis/flags/1x1/my.svg +11 -0
  177. data/app/assets/images/satis/flags/1x1/mz.svg +21 -0
  178. data/app/assets/images/satis/flags/1x1/na.svg +16 -0
  179. data/app/assets/images/satis/flags/1x1/nc.svg +14 -0
  180. data/app/assets/images/satis/flags/1x1/ne.svg +6 -0
  181. data/app/assets/images/satis/flags/1x1/nf.svg +11 -0
  182. data/app/assets/images/satis/flags/1x1/ng.svg +6 -0
  183. data/app/assets/images/satis/flags/1x1/ni.svg +129 -0
  184. data/app/assets/images/satis/flags/1x1/nl.svg +5 -0
  185. data/app/assets/images/satis/flags/1x1/no.svg +7 -0
  186. data/app/assets/images/satis/flags/1x1/np.svg +19 -0
  187. data/app/assets/images/satis/flags/1x1/nr.svg +12 -0
  188. data/app/assets/images/satis/flags/1x1/nu.svg +26 -0
  189. data/app/assets/images/satis/flags/1x1/nz.svg +42 -0
  190. data/app/assets/images/satis/flags/1x1/om.svg +115 -0
  191. data/app/assets/images/satis/flags/1x1/pa.svg +13 -0
  192. data/app/assets/images/satis/flags/1x1/pe.svg +244 -0
  193. data/app/assets/images/satis/flags/1x1/pf.svg +18 -0
  194. data/app/assets/images/satis/flags/1x1/pg.svg +16 -0
  195. data/app/assets/images/satis/flags/1x1/ph.svg +9 -0
  196. data/app/assets/images/satis/flags/1x1/pk.svg +15 -0
  197. data/app/assets/images/satis/flags/1x1/pl.svg +6 -0
  198. data/app/assets/images/satis/flags/1x1/pm.svg +7 -0
  199. data/app/assets/images/satis/flags/1x1/pn.svg +100 -0
  200. data/app/assets/images/satis/flags/1x1/pr.svg +13 -0
  201. data/app/assets/images/satis/flags/1x1/ps.svg +15 -0
  202. data/app/assets/images/satis/flags/1x1/pt.svg +57 -0
  203. data/app/assets/images/satis/flags/1x1/pw.svg +11 -0
  204. data/app/assets/images/satis/flags/1x1/py.svg +156 -0
  205. data/app/assets/images/satis/flags/1x1/qa.svg +4 -0
  206. data/app/assets/images/satis/flags/1x1/re.svg +7 -0
  207. data/app/assets/images/satis/flags/1x1/ro.svg +7 -0
  208. data/app/assets/images/satis/flags/1x1/rs.svg +296 -0
  209. data/app/assets/images/satis/flags/1x1/ru.svg +7 -0
  210. data/app/assets/images/satis/flags/1x1/rw.svg +13 -0
  211. data/app/assets/images/satis/flags/1x1/sa.svg +26 -0
  212. data/app/assets/images/satis/flags/1x1/sb.svg +13 -0
  213. data/app/assets/images/satis/flags/1x1/sc.svg +14 -0
  214. data/app/assets/images/satis/flags/1x1/sd.svg +13 -0
  215. data/app/assets/images/satis/flags/1x1/se.svg +5 -0
  216. data/app/assets/images/satis/flags/1x1/sg.svg +13 -0
  217. data/app/assets/images/satis/flags/1x1/sh.svg +81 -0
  218. data/app/assets/images/satis/flags/1x1/si.svg +18 -0
  219. data/app/assets/images/satis/flags/1x1/sj.svg +7 -0
  220. data/app/assets/images/satis/flags/1x1/sk.svg +9 -0
  221. data/app/assets/images/satis/flags/1x1/sl.svg +12 -0
  222. data/app/assets/images/satis/flags/1x1/sm.svg +89 -0
  223. data/app/assets/images/satis/flags/1x1/sn.svg +8 -0
  224. data/app/assets/images/satis/flags/1x1/so.svg +11 -0
  225. data/app/assets/images/satis/flags/1x1/sr.svg +6 -0
  226. data/app/assets/images/satis/flags/1x1/ss.svg +8 -0
  227. data/app/assets/images/satis/flags/1x1/st.svg +16 -0
  228. data/app/assets/images/satis/flags/1x1/sv.svg +594 -0
  229. data/app/assets/images/satis/flags/1x1/sx.svg +56 -0
  230. data/app/assets/images/satis/flags/1x1/sy.svg +6 -0
  231. data/app/assets/images/satis/flags/1x1/sz.svg +45 -0
  232. data/app/assets/images/satis/flags/1x1/ta.svg +81 -0
  233. data/app/assets/images/satis/flags/1x1/tc.svg +52 -0
  234. data/app/assets/images/satis/flags/1x1/td.svg +7 -0
  235. data/app/assets/images/satis/flags/1x1/tf.svg +15 -0
  236. data/app/assets/images/satis/flags/1x1/tg.svg +14 -0
  237. data/app/assets/images/satis/flags/1x1/th.svg +7 -0
  238. data/app/assets/images/satis/flags/1x1/tj.svg +26 -0
  239. data/app/assets/images/satis/flags/1x1/tk.svg +5 -0
  240. data/app/assets/images/satis/flags/1x1/tl.svg +13 -0
  241. data/app/assets/images/satis/flags/1x1/tm.svg +203 -0
  242. data/app/assets/images/satis/flags/1x1/tn.svg +13 -0
  243. data/app/assets/images/satis/flags/1x1/to.svg +10 -0
  244. data/app/assets/images/satis/flags/1x1/tr.svg +8 -0
  245. data/app/assets/images/satis/flags/1x1/tt.svg +7 -0
  246. data/app/assets/images/satis/flags/1x1/tv.svg +16 -0
  247. data/app/assets/images/satis/flags/1x1/tw.svg +32 -0
  248. data/app/assets/images/satis/flags/1x1/tz.svg +15 -0
  249. data/app/assets/images/satis/flags/1x1/ua.svg +6 -0
  250. data/app/assets/images/satis/flags/1x1/ug.svg +30 -0
  251. data/app/assets/images/satis/flags/1x1/um.svg +15 -0
  252. data/app/assets/images/satis/flags/1x1/un.svg +16 -0
  253. data/app/assets/images/satis/flags/1x1/us.svg +10 -0
  254. data/app/assets/images/satis/flags/1x1/uy.svg +28 -0
  255. data/app/assets/images/satis/flags/1x1/uz.svg +30 -0
  256. data/app/assets/images/satis/flags/1x1/va.svg +479 -0
  257. data/app/assets/images/satis/flags/1x1/vc.svg +8 -0
  258. data/app/assets/images/satis/flags/1x1/ve.svg +26 -0
  259. data/app/assets/images/satis/flags/1x1/vg.svg +127 -0
  260. data/app/assets/images/satis/flags/1x1/vi.svg +28 -0
  261. data/app/assets/images/satis/flags/1x1/vn.svg +11 -0
  262. data/app/assets/images/satis/flags/1x1/vu.svg +18 -0
  263. data/app/assets/images/satis/flags/1x1/wf.svg +7 -0
  264. data/app/assets/images/satis/flags/1x1/ws.svg +7 -0
  265. data/app/assets/images/satis/flags/1x1/xk.svg +16 -0
  266. data/app/assets/images/satis/flags/1x1/xx.svg +5 -0
  267. data/app/assets/images/satis/flags/1x1/ye.svg +7 -0
  268. data/app/assets/images/satis/flags/1x1/yt.svg +7 -0
  269. data/app/assets/images/satis/flags/1x1/za.svg +17 -0
  270. data/app/assets/images/satis/flags/1x1/zm.svg +27 -0
  271. data/app/assets/images/satis/flags/1x1/zw.svg +21 -0
  272. data/app/assets/images/satis/flags/4x3/ac.svg +76 -0
  273. data/app/assets/images/satis/flags/4x3/ad.svg +150 -0
  274. data/app/assets/images/satis/flags/4x3/ae.svg +6 -0
  275. data/app/assets/images/satis/flags/4x3/af.svg +81 -0
  276. data/app/assets/images/satis/flags/4x3/ag.svg +14 -0
  277. data/app/assets/images/satis/flags/4x3/ai.svg +763 -0
  278. data/app/assets/images/satis/flags/4x3/al.svg +5 -0
  279. data/app/assets/images/satis/flags/4x3/am.svg +5 -0
  280. data/app/assets/images/satis/flags/4x3/ao.svg +13 -0
  281. data/app/assets/images/satis/flags/4x3/aq.svg +5 -0
  282. data/app/assets/images/satis/flags/4x3/ar.svg +31 -0
  283. data/app/assets/images/satis/flags/4x3/as.svg +33 -0
  284. data/app/assets/images/satis/flags/4x3/at.svg +6 -0
  285. data/app/assets/images/satis/flags/4x3/au.svg +9 -0
  286. data/app/assets/images/satis/flags/4x3/aw.svg +186 -0
  287. data/app/assets/images/satis/flags/4x3/ax.svg +18 -0
  288. data/app/assets/images/satis/flags/4x3/az.svg +8 -0
  289. data/app/assets/images/satis/flags/4x3/ba.svg +12 -0
  290. data/app/assets/images/satis/flags/4x3/bb.svg +6 -0
  291. data/app/assets/images/satis/flags/4x3/bd.svg +4 -0
  292. data/app/assets/images/satis/flags/4x3/be.svg +7 -0
  293. data/app/assets/images/satis/flags/4x3/bf.svg +7 -0
  294. data/app/assets/images/satis/flags/4x3/bg.svg +7 -0
  295. data/app/assets/images/satis/flags/4x3/bh.svg +9 -0
  296. data/app/assets/images/satis/flags/4x3/bi.svg +15 -0
  297. data/app/assets/images/satis/flags/4x3/bj.svg +14 -0
  298. data/app/assets/images/satis/flags/4x3/bl.svg +7 -0
  299. data/app/assets/images/satis/flags/4x3/bm.svg +99 -0
  300. data/app/assets/images/satis/flags/4x3/bn.svg +36 -0
  301. data/app/assets/images/satis/flags/4x3/bo.svg +676 -0
  302. data/app/assets/images/satis/flags/4x3/bq.svg +5 -0
  303. data/app/assets/images/satis/flags/4x3/br.svg +45 -0
  304. data/app/assets/images/satis/flags/4x3/bs.svg +13 -0
  305. data/app/assets/images/satis/flags/4x3/bt.svg +89 -0
  306. data/app/assets/images/satis/flags/4x3/bv.svg +13 -0
  307. data/app/assets/images/satis/flags/4x3/bw.svg +7 -0
  308. data/app/assets/images/satis/flags/4x3/by.svg +20 -0
  309. data/app/assets/images/satis/flags/4x3/bz.svg +145 -0
  310. data/app/assets/images/satis/flags/4x3/ca.svg +4 -0
  311. data/app/assets/images/satis/flags/4x3/cc.svg +19 -0
  312. data/app/assets/images/satis/flags/4x3/cd.svg +5 -0
  313. data/app/assets/images/satis/flags/4x3/cf.svg +15 -0
  314. data/app/assets/images/satis/flags/4x3/cg.svg +12 -0
  315. data/app/assets/images/satis/flags/4x3/ch.svg +9 -0
  316. data/app/assets/images/satis/flags/4x3/ci.svg +7 -0
  317. data/app/assets/images/satis/flags/4x3/ck.svg +9 -0
  318. data/app/assets/images/satis/flags/4x3/cl.svg +13 -0
  319. data/app/assets/images/satis/flags/4x3/cm.svg +15 -0
  320. data/app/assets/images/satis/flags/4x3/cn.svg +11 -0
  321. data/app/assets/images/satis/flags/4x3/co.svg +7 -0
  322. data/app/assets/images/satis/flags/4x3/cp.svg +7 -0
  323. data/app/assets/images/satis/flags/4x3/cr.svg +7 -0
  324. data/app/assets/images/satis/flags/4x3/cu.svg +13 -0
  325. data/app/assets/images/satis/flags/4x3/cv.svg +13 -0
  326. data/app/assets/images/satis/flags/4x3/cw.svg +14 -0
  327. data/app/assets/images/satis/flags/4x3/cx.svg +15 -0
  328. data/app/assets/images/satis/flags/4x3/cy.svg +6 -0
  329. data/app/assets/images/satis/flags/4x3/cz.svg +5 -0
  330. data/app/assets/images/satis/flags/4x3/de.svg +5 -0
  331. data/app/assets/images/satis/flags/4x3/dg.svg +138 -0
  332. data/app/assets/images/satis/flags/4x3/dj.svg +13 -0
  333. data/app/assets/images/satis/flags/4x3/dk.svg +5 -0
  334. data/app/assets/images/satis/flags/4x3/dm.svg +152 -0
  335. data/app/assets/images/satis/flags/4x3/do.svg +6745 -0
  336. data/app/assets/images/satis/flags/4x3/dz.svg +5 -0
  337. data/app/assets/images/satis/flags/4x3/ea.svg +544 -0
  338. data/app/assets/images/satis/flags/4x3/ec.svg +138 -0
  339. data/app/assets/images/satis/flags/4x3/ee.svg +7 -0
  340. data/app/assets/images/satis/flags/4x3/eg.svg +38 -0
  341. data/app/assets/images/satis/flags/4x3/eh.svg +16 -0
  342. data/app/assets/images/satis/flags/4x3/er.svg +8 -0
  343. data/app/assets/images/satis/flags/4x3/es-ct.svg +4 -0
  344. data/app/assets/images/satis/flags/4x3/es-ga.svg +189 -0
  345. data/app/assets/images/satis/flags/4x3/es.svg +544 -0
  346. data/app/assets/images/satis/flags/4x3/et.svg +14 -0
  347. data/app/assets/images/satis/flags/4x3/eu.svg +28 -0
  348. data/app/assets/images/satis/flags/4x3/fi.svg +5 -0
  349. data/app/assets/images/satis/flags/4x3/fj.svg +122 -0
  350. data/app/assets/images/satis/flags/4x3/fk.svg +90 -0
  351. data/app/assets/images/satis/flags/4x3/fm.svg +11 -0
  352. data/app/assets/images/satis/flags/4x3/fo.svg +12 -0
  353. data/app/assets/images/satis/flags/4x3/fr.svg +7 -0
  354. data/app/assets/images/satis/flags/4x3/ga.svg +7 -0
  355. data/app/assets/images/satis/flags/4x3/gb-eng.svg +5 -0
  356. data/app/assets/images/satis/flags/4x3/gb-nir.svg +132 -0
  357. data/app/assets/images/satis/flags/4x3/gb-sct.svg +4 -0
  358. data/app/assets/images/satis/flags/4x3/gb-wls.svg +9 -0
  359. data/app/assets/images/satis/flags/4x3/gb.svg +7 -0
  360. data/app/assets/images/satis/flags/4x3/gd.svg +27 -0
  361. data/app/assets/images/satis/flags/4x3/ge.svg +6 -0
  362. data/app/assets/images/satis/flags/4x3/gf.svg +7 -0
  363. data/app/assets/images/satis/flags/4x3/gg.svg +9 -0
  364. data/app/assets/images/satis/flags/4x3/gh.svg +6 -0
  365. data/app/assets/images/satis/flags/4x3/gi.svg +32 -0
  366. data/app/assets/images/satis/flags/4x3/gl.svg +4 -0
  367. data/app/assets/images/satis/flags/4x3/gm.svg +14 -0
  368. data/app/assets/images/satis/flags/4x3/gn.svg +7 -0
  369. data/app/assets/images/satis/flags/4x3/gp.svg +7 -0
  370. data/app/assets/images/satis/flags/4x3/gq.svg +23 -0
  371. data/app/assets/images/satis/flags/4x3/gr.svg +16 -0
  372. data/app/assets/images/satis/flags/4x3/gs.svg +242 -0
  373. data/app/assets/images/satis/flags/4x3/gt.svg +204 -0
  374. data/app/assets/images/satis/flags/4x3/gu.svg +39 -0
  375. data/app/assets/images/satis/flags/4x3/gw.svg +13 -0
  376. data/app/assets/images/satis/flags/4x3/gy.svg +9 -0
  377. data/app/assets/images/satis/flags/4x3/hk.svg +30 -0
  378. data/app/assets/images/satis/flags/4x3/hm.svg +9 -0
  379. data/app/assets/images/satis/flags/4x3/hn.svg +18 -0
  380. data/app/assets/images/satis/flags/4x3/hr.svg +58 -0
  381. data/app/assets/images/satis/flags/4x3/ht.svg +116 -0
  382. data/app/assets/images/satis/flags/4x3/hu.svg +7 -0
  383. data/app/assets/images/satis/flags/4x3/ic.svg +7 -0
  384. data/app/assets/images/satis/flags/4x3/id.svg +6 -0
  385. data/app/assets/images/satis/flags/4x3/ie.svg +7 -0
  386. data/app/assets/images/satis/flags/4x3/il.svg +14 -0
  387. data/app/assets/images/satis/flags/4x3/im.svg +36 -0
  388. data/app/assets/images/satis/flags/4x3/in.svg +25 -0
  389. data/app/assets/images/satis/flags/4x3/io.svg +138 -0
  390. data/app/assets/images/satis/flags/4x3/iq.svg +10 -0
  391. data/app/assets/images/satis/flags/4x3/ir.svg +219 -0
  392. data/app/assets/images/satis/flags/4x3/is.svg +12 -0
  393. data/app/assets/images/satis/flags/4x3/it.svg +7 -0
  394. data/app/assets/images/satis/flags/4x3/je.svg +47 -0
  395. data/app/assets/images/satis/flags/4x3/jm.svg +8 -0
  396. data/app/assets/images/satis/flags/4x3/jo.svg +16 -0
  397. data/app/assets/images/satis/flags/4x3/jp.svg +11 -0
  398. data/app/assets/images/satis/flags/4x3/ke.svg +23 -0
  399. data/app/assets/images/satis/flags/4x3/kg.svg +15 -0
  400. data/app/assets/images/satis/flags/4x3/kh.svg +61 -0
  401. data/app/assets/images/satis/flags/4x3/ki.svg +36 -0
  402. data/app/assets/images/satis/flags/4x3/km.svg +16 -0
  403. data/app/assets/images/satis/flags/4x3/kn.svg +14 -0
  404. data/app/assets/images/satis/flags/4x3/kp.svg +15 -0
  405. data/app/assets/images/satis/flags/4x3/kr.svg +24 -0
  406. data/app/assets/images/satis/flags/4x3/kw.svg +13 -0
  407. data/app/assets/images/satis/flags/4x3/ky.svg +225 -0
  408. data/app/assets/images/satis/flags/4x3/kz.svg +23 -0
  409. data/app/assets/images/satis/flags/4x3/la.svg +12 -0
  410. data/app/assets/images/satis/flags/4x3/lb.svg +15 -0
  411. data/app/assets/images/satis/flags/4x3/lc.svg +8 -0
  412. data/app/assets/images/satis/flags/4x3/li.svg +43 -0
  413. data/app/assets/images/satis/flags/4x3/lk.svg +22 -0
  414. data/app/assets/images/satis/flags/4x3/lr.svg +14 -0
  415. data/app/assets/images/satis/flags/4x3/ls.svg +8 -0
  416. data/app/assets/images/satis/flags/4x3/lt.svg +7 -0
  417. data/app/assets/images/satis/flags/4x3/lu.svg +5 -0
  418. data/app/assets/images/satis/flags/4x3/lv.svg +6 -0
  419. data/app/assets/images/satis/flags/4x3/ly.svg +13 -0
  420. data/app/assets/images/satis/flags/4x3/ma.svg +4 -0
  421. data/app/assets/images/satis/flags/4x3/mc.svg +6 -0
  422. data/app/assets/images/satis/flags/4x3/md.svg +70 -0
  423. data/app/assets/images/satis/flags/4x3/me.svg +116 -0
  424. data/app/assets/images/satis/flags/4x3/mf.svg +7 -0
  425. data/app/assets/images/satis/flags/4x3/mg.svg +7 -0
  426. data/app/assets/images/satis/flags/4x3/mh.svg +7 -0
  427. data/app/assets/images/satis/flags/4x3/mk.svg +5 -0
  428. data/app/assets/images/satis/flags/4x3/ml.svg +7 -0
  429. data/app/assets/images/satis/flags/4x3/mm.svg +16 -0
  430. data/app/assets/images/satis/flags/4x3/mn.svg +13 -0
  431. data/app/assets/images/satis/flags/4x3/mo.svg +9 -0
  432. data/app/assets/images/satis/flags/4x3/mp.svg +86 -0
  433. data/app/assets/images/satis/flags/4x3/mq.svg +7 -0
  434. data/app/assets/images/satis/flags/4x3/mr.svg +6 -0
  435. data/app/assets/images/satis/flags/4x3/ms.svg +78 -0
  436. data/app/assets/images/satis/flags/4x3/mt.svg +49 -0
  437. data/app/assets/images/satis/flags/4x3/mu.svg +8 -0
  438. data/app/assets/images/satis/flags/4x3/mv.svg +6 -0
  439. data/app/assets/images/satis/flags/4x3/mw.svg +10 -0
  440. data/app/assets/images/satis/flags/4x3/mx.svg +382 -0
  441. data/app/assets/images/satis/flags/4x3/my.svg +11 -0
  442. data/app/assets/images/satis/flags/4x3/mz.svg +21 -0
  443. data/app/assets/images/satis/flags/4x3/na.svg +16 -0
  444. data/app/assets/images/satis/flags/4x3/nc.svg +14 -0
  445. data/app/assets/images/satis/flags/4x3/ne.svg +6 -0
  446. data/app/assets/images/satis/flags/4x3/nf.svg +9 -0
  447. data/app/assets/images/satis/flags/4x3/ng.svg +6 -0
  448. data/app/assets/images/satis/flags/4x3/ni.svg +129 -0
  449. data/app/assets/images/satis/flags/4x3/nl.svg +5 -0
  450. data/app/assets/images/satis/flags/4x3/no.svg +7 -0
  451. data/app/assets/images/satis/flags/4x3/np.svg +14 -0
  452. data/app/assets/images/satis/flags/4x3/nr.svg +12 -0
  453. data/app/assets/images/satis/flags/4x3/nu.svg +26 -0
  454. data/app/assets/images/satis/flags/4x3/nz.svg +42 -0
  455. data/app/assets/images/satis/flags/4x3/om.svg +115 -0
  456. data/app/assets/images/satis/flags/4x3/pa.svg +14 -0
  457. data/app/assets/images/satis/flags/4x3/pe.svg +244 -0
  458. data/app/assets/images/satis/flags/4x3/pf.svg +19 -0
  459. data/app/assets/images/satis/flags/4x3/pg.svg +9 -0
  460. data/app/assets/images/satis/flags/4x3/ph.svg +9 -0
  461. data/app/assets/images/satis/flags/4x3/pk.svg +15 -0
  462. data/app/assets/images/satis/flags/4x3/pl.svg +6 -0
  463. data/app/assets/images/satis/flags/4x3/pm.svg +7 -0
  464. data/app/assets/images/satis/flags/4x3/pn.svg +97 -0
  465. data/app/assets/images/satis/flags/4x3/pr.svg +13 -0
  466. data/app/assets/images/satis/flags/4x3/ps.svg +15 -0
  467. data/app/assets/images/satis/flags/4x3/pt.svg +57 -0
  468. data/app/assets/images/satis/flags/4x3/pw.svg +11 -0
  469. data/app/assets/images/satis/flags/4x3/py.svg +157 -0
  470. data/app/assets/images/satis/flags/4x3/qa.svg +4 -0
  471. data/app/assets/images/satis/flags/4x3/re.svg +7 -0
  472. data/app/assets/images/satis/flags/4x3/ro.svg +7 -0
  473. data/app/assets/images/satis/flags/4x3/rs.svg +292 -0
  474. data/app/assets/images/satis/flags/4x3/ru.svg +7 -0
  475. data/app/assets/images/satis/flags/4x3/rw.svg +13 -0
  476. data/app/assets/images/satis/flags/4x3/sa.svg +26 -0
  477. data/app/assets/images/satis/flags/4x3/sb.svg +13 -0
  478. data/app/assets/images/satis/flags/4x3/sc.svg +14 -0
  479. data/app/assets/images/satis/flags/4x3/sd.svg +13 -0
  480. data/app/assets/images/satis/flags/4x3/se.svg +5 -0
  481. data/app/assets/images/satis/flags/4x3/sg.svg +13 -0
  482. data/app/assets/images/satis/flags/4x3/sh.svg +76 -0
  483. data/app/assets/images/satis/flags/4x3/si.svg +18 -0
  484. data/app/assets/images/satis/flags/4x3/sj.svg +7 -0
  485. data/app/assets/images/satis/flags/4x3/sk.svg +9 -0
  486. data/app/assets/images/satis/flags/4x3/sl.svg +7 -0
  487. data/app/assets/images/satis/flags/4x3/sm.svg +91 -0
  488. data/app/assets/images/satis/flags/4x3/sn.svg +8 -0
  489. data/app/assets/images/satis/flags/4x3/so.svg +11 -0
  490. data/app/assets/images/satis/flags/4x3/sr.svg +6 -0
  491. data/app/assets/images/satis/flags/4x3/ss.svg +8 -0
  492. data/app/assets/images/satis/flags/4x3/st.svg +16 -0
  493. data/app/assets/images/satis/flags/4x3/sv.svg +594 -0
  494. data/app/assets/images/satis/flags/4x3/sx.svg +56 -0
  495. data/app/assets/images/satis/flags/4x3/sy.svg +6 -0
  496. data/app/assets/images/satis/flags/4x3/sz.svg +45 -0
  497. data/app/assets/images/satis/flags/4x3/ta.svg +76 -0
  498. data/app/assets/images/satis/flags/4x3/tc.svg +52 -0
  499. data/app/assets/images/satis/flags/4x3/td.svg +7 -0
  500. data/app/assets/images/satis/flags/4x3/tf.svg +15 -0
  501. data/app/assets/images/satis/flags/4x3/tg.svg +14 -0
  502. data/app/assets/images/satis/flags/4x3/th.svg +7 -0
  503. data/app/assets/images/satis/flags/4x3/tj.svg +22 -0
  504. data/app/assets/images/satis/flags/4x3/tk.svg +5 -0
  505. data/app/assets/images/satis/flags/4x3/tl.svg +13 -0
  506. data/app/assets/images/satis/flags/4x3/tm.svg +206 -0
  507. data/app/assets/images/satis/flags/4x3/tn.svg +13 -0
  508. data/app/assets/images/satis/flags/4x3/to.svg +10 -0
  509. data/app/assets/images/satis/flags/4x3/tr.svg +8 -0
  510. data/app/assets/images/satis/flags/4x3/tt.svg +5 -0
  511. data/app/assets/images/satis/flags/4x3/tv.svg +16 -0
  512. data/app/assets/images/satis/flags/4x3/tw.svg +34 -0
  513. data/app/assets/images/satis/flags/4x3/tz.svg +13 -0
  514. data/app/assets/images/satis/flags/4x3/ua.svg +6 -0
  515. data/app/assets/images/satis/flags/4x3/ug.svg +30 -0
  516. data/app/assets/images/satis/flags/4x3/um.svg +15 -0
  517. data/app/assets/images/satis/flags/4x3/un.svg +16 -0
  518. data/app/assets/images/satis/flags/4x3/us.svg +10 -0
  519. data/app/assets/images/satis/flags/4x3/uy.svg +28 -0
  520. data/app/assets/images/satis/flags/4x3/uz.svg +30 -0
  521. data/app/assets/images/satis/flags/4x3/va.svg +479 -0
  522. data/app/assets/images/satis/flags/4x3/vc.svg +8 -0
  523. data/app/assets/images/satis/flags/4x3/ve.svg +26 -0
  524. data/app/assets/images/satis/flags/4x3/vg.svg +127 -0
  525. data/app/assets/images/satis/flags/4x3/vi.svg +28 -0
  526. data/app/assets/images/satis/flags/4x3/vn.svg +11 -0
  527. data/app/assets/images/satis/flags/4x3/vu.svg +18 -0
  528. data/app/assets/images/satis/flags/4x3/wf.svg +7 -0
  529. data/app/assets/images/satis/flags/4x3/ws.svg +7 -0
  530. data/app/assets/images/satis/flags/4x3/xk.svg +16 -0
  531. data/app/assets/images/satis/flags/4x3/xx.svg +5 -0
  532. data/app/assets/images/satis/flags/4x3/ye.svg +7 -0
  533. data/app/assets/images/satis/flags/4x3/yt.svg +7 -0
  534. data/app/assets/images/satis/flags/4x3/za.svg +17 -0
  535. data/app/assets/images/satis/flags/4x3/zm.svg +27 -0
  536. data/app/assets/images/satis/flags/4x3/zw.svg +21 -0
  537. data/app/assets/images/satis/intl-tel-input/flags.png +0 -0
  538. data/app/assets/images/satis/intl-tel-input/flags@2x.png +0 -0
  539. data/app/assets/images/satis/leaflet/layers-2x.png +0 -0
  540. data/app/assets/images/satis/leaflet/layers.png +0 -0
  541. data/app/assets/images/satis/leaflet/marker-icon-2x.png +0 -0
  542. data/app/assets/images/satis/leaflet/marker-icon.png +0 -0
  543. data/app/assets/images/satis/leaflet/marker-shadow.png +0 -0
  544. data/app/assets/stylesheets/satis/application.css +68 -15
  545. data/app/assets/stylesheets/satis/components/diffy.css +74 -0
  546. data/app/assets/stylesheets/satis/components/flag-icons.min.css +1 -0
  547. data/app/assets/stylesheets/satis/components/form.css +158 -0
  548. data/app/assets/stylesheets/satis/components/intlTelInput.min.css +1 -0
  549. data/app/assets/stylesheets/satis/components/leaflet.css +661 -0
  550. data/app/assets/stylesheets/satis/components/phone-number.css +30 -0
  551. data/app/assets/stylesheets/satis/components/tippyjs.css +8 -0
  552. data/app/assets/stylesheets/satis/components/trix.css +412 -0
  553. data/app/components/satis/appearance_switcher/{component.scss → component.css} +1 -1
  554. data/app/components/satis/appearance_switcher/component.html.slim +1 -1
  555. data/app/components/satis/appearance_switcher/component_controller.js +3 -4
  556. data/app/components/satis/application_component.rb +8 -42
  557. data/app/components/satis/avatar/component.rb +3 -1
  558. data/app/components/satis/calendar_month/component.html.slim +522 -0
  559. data/app/components/satis/calendar_month/component.rb +9 -0
  560. data/app/components/satis/card/component.css +19 -0
  561. data/app/components/satis/card/component.html.slim +8 -32
  562. data/app/components/satis/card/component.rb +35 -8
  563. data/app/components/satis/date_time_picker/component.rb +10 -10
  564. data/app/components/satis/date_time_picker/component_controller.js +61 -45
  565. data/app/components/satis/dialog/component.html.slim +26 -0
  566. data/app/components/satis/dialog/component.rb +22 -0
  567. data/app/components/satis/dialog/component_controller.js +16 -0
  568. data/app/components/satis/dropdown/{component.scss → component.css} +5 -1
  569. data/app/components/satis/dropdown/component.html.slim +28 -14
  570. data/app/components/satis/dropdown/component.rb +44 -7
  571. data/app/components/satis/dropdown/component_controller.js +568 -172
  572. data/app/components/satis/editor/component.css +3 -0
  573. data/app/components/satis/editor/component.html.slim +5 -0
  574. data/app/components/satis/editor/component.rb +45 -0
  575. data/app/components/satis/editor/component_controller.js +90 -0
  576. data/app/components/satis/flash_messages/{component.scss → component.css} +5 -4
  577. data/app/components/satis/info_item/component.html.slim +2 -2
  578. data/app/components/satis/info_item/component.rb +10 -2
  579. data/app/components/satis/input/component_controller.js +20 -0
  580. data/app/components/satis/input_array/component.css +3 -0
  581. data/app/components/satis/input_array/component.html.slim +33 -0
  582. data/app/components/satis/input_array/component.rb +18 -0
  583. data/app/components/satis/input_array/component_controller.js +54 -0
  584. data/app/components/satis/map/{component.scss → component.css} +1 -1
  585. data/app/components/satis/map/component.html.slim +1 -1
  586. data/app/components/satis/map/component_controller.js +14 -12
  587. data/app/components/satis/menu/component.html.slim +4 -5
  588. data/app/components/satis/menu/component.rb +8 -3
  589. data/app/components/satis/menu/component_controller.js +139 -17
  590. data/app/components/satis/menu_item/component.html.slim +13 -8
  591. data/app/components/satis/menu_item/component.rb +1 -0
  592. data/app/components/satis/page/component.html.slim +30 -12
  593. data/app/components/satis/page/component_controller.js +2 -2
  594. data/app/components/satis/progress_bar/component.html.slim +4 -0
  595. data/app/components/satis/progress_bar/component.rb +57 -0
  596. data/app/components/satis/sidebar_menu/component.css +0 -0
  597. data/app/components/satis/sidebar_menu/component.html.slim +2 -3
  598. data/app/components/satis/sidebar_menu/component_controller.js +3 -4
  599. data/app/components/satis/sidebar_menu/mobile/component.html.slim +1 -2
  600. data/app/components/satis/sidebar_menu_item/component.html.slim +8 -5
  601. data/app/components/satis/sidebar_menu_item/component.rb +1 -0
  602. data/app/components/satis/sidebar_menu_item/component_controller.js +13 -4
  603. data/app/components/satis/switch/component_controller.js +3 -4
  604. data/app/components/satis/tab/component.rb +41 -17
  605. data/app/components/satis/tabs/{component.scss → component.css} +17 -1
  606. data/app/components/satis/tabs/component.html.slim +23 -9
  607. data/app/components/satis/tabs/component.rb +9 -2
  608. data/app/components/satis/tabs/component_controller.js +34 -19
  609. data/app/controllers/satis/dialogs_controller.rb +20 -0
  610. data/app/controllers/satis/documentation/avatars_controller.rb +7 -0
  611. data/app/controllers/satis/documentation/cards_controller.rb +6 -0
  612. data/app/controllers/satis/documentation/editors_controller.rb +12 -0
  613. data/app/controllers/satis/documentation/forms_controller.rb +14 -0
  614. data/app/controllers/satis/documentation/tabs_controller.rb +7 -0
  615. data/app/controllers/satis/documentation_controller.rb +6 -0
  616. data/app/controllers/satis/user_data_controller.rb +5 -4
  617. data/app/helpers/satis/showcases/avatars_helper.rb +4 -0
  618. data/app/javascript/satis/application.js +8 -0
  619. data/app/javascript/satis/controllers/application.js +12 -0
  620. data/app/javascript/satis/controllers/application_controller.js +71 -0
  621. data/app/javascript/satis/controllers/fields_for_controller.js +74 -0
  622. data/app/javascript/satis/controllers/file_controller.js +26 -0
  623. data/app/javascript/satis/controllers/form_controller.js +77 -0
  624. data/app/javascript/satis/controllers/index.js +70 -0
  625. data/app/javascript/satis/controllers/link_controller.js +34 -0
  626. data/app/javascript/satis/controllers/phone_number_controller.js +30 -0
  627. data/app/javascript/satis/elements/copyable_element.js +57 -0
  628. data/app/javascript/satis/utility_controllers/draggable_controller.js +144 -0
  629. data/app/javascript/satis/utility_controllers/help_controller.js +18 -0
  630. data/app/javascript/satis/utility_controllers/index.js +14 -0
  631. data/app/javascript/satis/utility_controllers/show_hide_controller.js +71 -0
  632. data/app/javascript/satis/utility_controllers/toggle_controller.js +100 -0
  633. data/app/javascript/satis/utils.js +41 -0
  634. data/app/models/satis/user_data.rb +3 -1
  635. data/app/views/satis/documentation/avatars/index.html.slim +39 -0
  636. data/app/views/satis/documentation/cards/index.html.slim +72 -0
  637. data/app/views/satis/documentation/editors/index.html.slim +44 -0
  638. data/app/views/satis/documentation/forms/index.html.slim +109 -0
  639. data/app/views/satis/documentation/forms/select.html.slim +7 -0
  640. data/app/views/satis/documentation/index.html.slim +6 -0
  641. data/app/views/satis/documentation/tabs/index.html.slim +31 -0
  642. data/app/views/shared/_fields_for.html.slim +3 -3
  643. data/config/importmap.rb +49 -0
  644. data/config/locales/en.yml +9 -0
  645. data/config/routes.rb +14 -0
  646. data/db/migrate/20221212083110_change_satis_user_data.rb +2 -2
  647. data/db/migrate/20230918115448_add_type_to_satis_user_data.rb +10 -0
  648. data/lib/generators/satis/install_generator.rb +22 -0
  649. data/lib/generators/satis/tailwind_config_generator.rb +24 -0
  650. data/lib/generators/satis/templates/config/initializers/satis.rb +13 -0
  651. data/lib/satis/concerns/contextual_translations.rb +58 -0
  652. data/lib/satis/configuration.rb +51 -30
  653. data/lib/satis/engine.rb +25 -10
  654. data/lib/satis/forms/builder.rb +130 -87
  655. data/lib/satis/forms/concerns/buttons.rb +2 -2
  656. data/lib/satis/forms/concerns/file.rb +1 -1
  657. data/lib/satis/helpers/container.rb +9 -7
  658. data/lib/satis/menus/item.rb +1 -1
  659. data/lib/satis/menus/menu.rb +3 -2
  660. data/lib/satis/version.rb +1 -1
  661. data/lib/satis.rb +2 -10
  662. data/lib/tasks/satis_tasks.rake +13 -4
  663. metadata +706 -20
  664. data/app/components/satis/card/component.md +0 -14
  665. data/app/components/satis/card/component.scss +0 -15
  666. /data/app/components/satis/breadcrumbs/{component.scss → component.css} +0 -0
  667. /data/app/components/satis/date_time_picker/{component.scss → component.css} +0 -0
  668. /data/app/components/satis/{sidebar_menu/component.scss → dialog/component.css} +0 -0
  669. /data/app/components/satis/input/{component.scss → component.css} +0 -0
  670. /data/app/components/satis/menu/{component.scss → component.css} +0 -0
  671. /data/app/components/satis/sidebar_menu_item/{component.scss → component.css} +0 -0
@@ -1,11 +1,24 @@
1
- import ApplicationController from "../../../../frontend/controllers/application_controller"
1
+ import ApplicationController from "satis/controllers/application_controller"
2
2
 
3
- // FIXME: Is this full path really needed?
4
- import { debounce, popperSameWidth } from "../../../../frontend/utils"
3
+ import { debounce, popperSameWidth } from "satis/utils"
5
4
  import { createPopper } from "@popperjs/core"
6
5
 
7
- export default class extends ApplicationController {
8
- static targets = ["results", "items", "item", "searchInput", "resetButton", "toggleButton", "hiddenInput"]
6
+ export default class DropdownComponentController extends ApplicationController {
7
+
8
+ static targets = [
9
+ "results",
10
+ "items",
11
+ "item",
12
+ "searchInput",
13
+ "resetButton",
14
+ "toggleButton",
15
+ "hiddenSelect",
16
+ "pills",
17
+ "pillTemplate",
18
+ "pill",
19
+ "selectedItemsTemplate"
20
+ ]
21
+
9
22
  static values = {
10
23
  chainTo: String,
11
24
  freeText: Boolean,
@@ -13,19 +26,19 @@ export default class extends ApplicationController {
13
26
  pageSize: Number,
14
27
  url: String,
15
28
  urlParams: Object,
29
+ isMultiple: Boolean
16
30
  }
17
31
 
18
32
  connect() {
19
33
  super.connect()
20
-
21
34
  this.debouncedFetchResults = debounce(this.fetchResults.bind(this), 250)
22
35
  this.debouncedLocalResults = debounce(this.localResults.bind(this), 250)
23
36
  this.selectedIndex = -1
24
37
 
25
38
  this.boundClickedOutside = this.clickedOutside.bind(this)
26
39
  this.boundResetSearchInput = this.resetSearchInput.bind(this)
27
- this.boundHandleHiddenInputChange = this.handleHiddenInputChange.bind(this)
28
- this.boundBlur = this.handleBlur.bind(this)
40
+ this.boundBlur = this.blur.bind(this)
41
+ this.boundChainToChanged = this.chainToChanged.bind(this)
29
42
 
30
43
  // To remember what the current page and last page were, we queried
31
44
  this.currentPage = 1
@@ -33,42 +46,102 @@ export default class extends ApplicationController {
33
46
  this.endPage = null
34
47
 
35
48
  // To remember what the last search was we did
49
+ this.searchQueryValue = null
36
50
  this.lastSearch = null
51
+ this.minSearchQueryLength = 2
37
52
 
38
- this.display()
53
+ // To remember what the last options were we got from the server to prevent unnecessary refreshes
54
+ // and unexpected events
55
+ this.lastServerRefreshOptions = new Set()
39
56
 
40
57
  this.popperInstance = createPopper(this.element, this.resultsTarget, {
41
- offset: [-20, 2],
42
58
  placement: "bottom-start",
59
+ strategy: "fixed",
43
60
  modifiers: [
61
+ { name: "offset", options: { offset: [0, 1] } },
44
62
  {
45
63
  name: "flip",
46
- enabled: true,
47
64
  options: {
48
- boundary: this.element.closest(".satis-card"),
65
+ fallbackPlacements: ["bottom"],
66
+ boundary: this.element.closest(".sts-card"),
49
67
  },
50
68
  },
51
69
  {
52
70
  name: "preventOverflow",
53
- enabled: true,
71
+ options: {
72
+ boundary: this.element.closest(".sts-card"),
73
+ },
54
74
  },
55
75
  popperSameWidth,
56
76
  ],
57
77
  })
78
+ this.popperInstance.state.elements.popper.popperInstance = () => this.popperInstance
58
79
 
80
+ if (this.hasToggleButtonTarget) this.toggleButtonTarget.addEventListener("blur", this.boundBlur)
59
81
  this.searchInputTarget.addEventListener("blur", this.boundBlur)
60
- this.toggleButtonTarget.addEventListener("blur", this.boundBlur)
61
82
  this.resultsTarget.addEventListener("blur", this.boundBlur)
62
83
 
63
84
  window.addEventListener("click", this.boundClickedOutside)
64
85
 
65
- this.hiddenInputTarget.addEventListener("change", this.boundHandleHiddenInputChange)
86
+ setTimeout(() => {
87
+ this.getScrollParent(this.element)?.addEventListener("scroll", this.boundBlur)
88
+ }, 500)
89
+
90
+ if (this.chainToValue) {
91
+ this.getChainToElement()?.addEventListener("change", this.boundChainToChanged)
92
+ }
93
+
94
+ if (this.hiddenSelectTarget.selectedOptions.length > 0 && this.hiddenSelectTarget.selectedOptions[0].value) {
95
+ this.refreshSelectionFromServer().then((changed) => {
96
+ this.filterResultsChainTo()
97
+ this.setHiddenSelect()
98
+
99
+ if (!this.hiddenSelectTarget.getAttribute("data-reflex")) {
100
+ let event = new Event("change")
101
+ event.detail = { src: "satis-dropdown" }
102
+ this.hiddenSelectTarget.dispatchEvent(event)
103
+ }
104
+ })
105
+ }
106
+ }
107
+
108
+ getScrollParent(node) {
109
+ if (node == null) {
110
+ return null
111
+ }
112
+
113
+ let isScrollable = false
114
+
115
+ if (node instanceof Element) {
116
+ const vScrollValue = window.getComputedStyle(node).getPropertyValue("overflow-y")
117
+
118
+ isScrollable = vScrollValue == "auto" || vScrollValue == "scroll"
119
+ }
120
+
121
+ if (isScrollable) {
122
+ return node
123
+ } else {
124
+ return node.parentNode == null ? node : this.getScrollParent(node.parentNode)
125
+ }
126
+ }
127
+
128
+ chainToChanged(event) {
129
+ // Ignore if we triggered this change event
130
+ if (event?.detail?.src == "satis-dropdown") {
131
+ return
132
+ }
133
+
134
+ this.reset(event)
66
135
  }
67
136
 
68
137
  disconnect() {
69
138
  this.debouncedFetchResults = null
70
139
  this.debouncedLocalResults = null
71
140
  window.removeEventListener("click", this.boundClickedOutside)
141
+ this.getChainToElement()?.removeEventListener("change", this.boundChainToChanged)
142
+ if (this.hasToggleButtonTarget) this.toggleButtonTarget.removeEventListener("blur", this.boundBlur)
143
+ this.resultsTarget.removeEventListener("blur", this.boundBlur)
144
+ this.searchInputTarget.removeEventListener("blur", this.boundBlur)
72
145
  }
73
146
 
74
147
  focus(event) {
@@ -76,27 +149,15 @@ export default class extends ApplicationController {
76
149
  }
77
150
 
78
151
  blur(event) {
79
- this.handleBlur(event)
80
- }
81
-
82
- handleBlur(event) {
83
- if (!this.element.contains(event.relatedTarget) && this.resultsShown) {
84
- this.hideResultsList()
85
- if (event.target == this.searchInputTarget) {
86
- this.boundResetSearchInput(event)
87
- }
152
+ let target = event.relatedTarget
153
+ if (target == null) {
154
+ target = document.target
88
155
  }
89
- }
90
156
 
91
- handleHiddenInputChange(event) {
92
- if (event?.detail?.src == "satis-dropdown") {
93
- return
94
- }
95
-
96
- if (this.hiddenInputTarget.value == "") {
97
- this.searchInputTarget.value = null
98
- } else {
99
- this.resetSearchInput()
157
+ if (event.type != "scroll" && !this.element.contains(target)) {
158
+ if (this.resultsShown)
159
+ this.hideResultsList()
160
+ this.boundResetSearchInput(event)
100
161
  }
101
162
  }
102
163
 
@@ -108,25 +169,19 @@ export default class extends ApplicationController {
108
169
  return
109
170
  }
110
171
 
111
- // Put current selection in search field
112
- if (this.hiddenInputTarget.value) {
113
- if (this.itemTargets.length == 0) {
114
- let ourUrl = this.normalizedUrl
115
- ourUrl.searchParams.append("id", this.hiddenInputTarget.value)
116
- ourUrl.searchParams.append("page", this.currentPage)
117
- ourUrl.searchParams.append("page_size", this.pageSizeValue)
172
+ this.refreshSelectionFromServer().then(() => {
173
+ // resolved
174
+ this.setHiddenSelect()
118
175
 
119
- this.fetchResultsWith(ourUrl).then(() => {
120
- this.setHiddenInput()
121
- })
122
- } else {
123
- this.setHiddenInput()
176
+ if (!this.searchInputTarget.value && this.freeTextValue && this.hiddenSelectTarget.options.length > 0) {
177
+ this.searchInputTarget.value = this.hiddenSelectTarget.options[0].value
124
178
  }
125
179
 
126
- if (!this.searchInputTarget.value && this.freeTextValue) {
127
- this.searchInputTarget.value = this.hiddenInputTarget.value
128
- }
129
- }
180
+ if (!this.hiddenSelectTarget.getAttribute("data-reflex"))
181
+ this.hiddenSelectTarget.dispatchEvent(new CustomEvent("change", { detail: { src: "satis-dropdown" } }))
182
+
183
+ this.validateSearchQuery()
184
+ })
130
185
  }
131
186
 
132
187
  // Called when scrolling in the resultsTarget
@@ -142,24 +197,29 @@ export default class extends ApplicationController {
142
197
  return
143
198
  }
144
199
 
145
- this.filterResultsChainTo()
146
-
147
200
  switch (event.key) {
148
201
  case "ArrowDown":
149
202
  if (this.hasResults) {
150
- this.showResultsList(event)
151
-
203
+ if (!this.resultsShown)
204
+ this.showResultsList(event)
152
205
  this.moveDown()
153
206
  }
207
+ // prevent the cursor from jumping to the beginning of the input and scrolling in some cases
208
+ event.preventDefault()
154
209
  break
155
210
  case "ArrowUp":
156
211
  if (this.hasResults) {
157
212
  this.moveUp()
158
213
  }
214
+ // prevent the cursor from jumping to the beginning of the input and scrolling in some cases
215
+ event.preventDefault()
159
216
  break
160
217
  case "Enter":
161
218
  event.preventDefault()
162
219
  this.select(event)
220
+ // expect the dropdown to hide when its a freetext value
221
+ if (this.selectedIndex === -1 && this.freeTextValue)
222
+ this.hideResultsList(event)
163
223
 
164
224
  break
165
225
  case "Escape":
@@ -179,48 +239,68 @@ export default class extends ApplicationController {
179
239
 
180
240
  // User enters text in the search field
181
241
  search(event) {
242
+ this.searchQueryValue = this.searchInputTarget.value
243
+
244
+ if(this.searchInputTarget.value.length === 0 && !this.isMultipleValue){
245
+ if(this.nrOfItems === 1) this.lowLightSelected();
246
+ this.hiddenSelectTarget.innerHTML = ""
247
+ this.hiddenSelectTarget.add(this.createOption())
248
+ }
249
+
182
250
  if (this.hasUrlValue) {
183
251
  this.debouncedFetchResults(event)
184
252
  } else {
185
253
  this.debouncedLocalResults(event)
186
254
  }
187
255
 
188
- if (this.searchInputTarget.value) {
189
- this.searchInputTarget.closest(".bg-white").classList.add("warning")
190
- } else {
191
- this.searchInputTarget.closest(".bg-white").classList.remove("warning")
256
+ if (!this.isMultipleValue) {
257
+ // set the freetext value as the selected value
258
+ if (this.freeTextValue && this.searchInputTarget.value) {
259
+ this.hiddenSelectTarget.innerHTML = ""
260
+ var option = this.createOption({ text: this.searchInputTarget.value, value: this.searchInputTarget.value })
261
+ this.hiddenSelectTarget.add(option)
262
+ }
192
263
  }
193
264
  }
194
265
 
195
266
  // User presses reset button
196
267
  reset(event) {
197
- this.hiddenInputTarget.value = null
198
- this.hiddenInputTarget.dispatchEvent(new Event("change"))
199
- this.searchInputTarget.value = null
268
+ if (!this.isMultipleValue) {
269
+ this.hiddenSelectTarget.innerHTML = ""
270
+ this.lastServerRefreshOptions.clear()
271
+ this.selectedItemsTemplateTarget.innerHTML = ""
272
+ this.hiddenSelectTarget.options.add(this.createOption())
273
+ }
274
+
275
+ this.searchInputTarget.value = ""
276
+ this.searchQueryValue = null
277
+ this.currentPage = 1
200
278
  this.lastSearch = null
201
279
  this.lastPage = null
202
280
  this.endPage = null
203
281
 
204
- if (this.selectedItem) {
205
- this.selectedItem.classList.remove("bg-primary-200")
206
- }
282
+ this.lowLightSelected();
207
283
  this.selectedIndex = -1
284
+
208
285
  if (this.hasUrlValue) {
209
286
  this.itemsTarget.innerHTML = ""
210
287
  }
211
- this.hideResultsList()
288
+
212
289
  this.itemTargets.forEach((item) => {
213
290
  item.classList.remove("hidden")
214
291
  })
292
+ this.filterResultsChainTo()
293
+
294
+ // hide all results and reset
295
+ this.hideResultsList()
296
+
297
+ this.validateSearchQuery()
215
298
 
216
299
  if (event) {
217
300
  event.preventDefault()
218
301
  }
219
302
 
220
- if (this.searchInputTarget.closest(".bg-white").classList.contains("warning")) {
221
- this.searchInputTarget.closest(".bg-white").classList.remove("warning")
222
- }
223
-
303
+ this.hiddenSelectTarget.dispatchEvent(new Event("change"))
224
304
  return false
225
305
  }
226
306
 
@@ -230,59 +310,106 @@ export default class extends ApplicationController {
230
310
  if (dataDiv == null) {
231
311
  dataDiv = this.selectedItem
232
312
  }
313
+ if (dataDiv == null) return
233
314
 
234
- if (dataDiv == null) {
235
- return
236
- }
237
-
238
- this.selectItem(dataDiv)
315
+ this.selectItem(dataDiv, true)
239
316
 
240
317
  event.preventDefault()
241
318
  }
242
319
 
243
- selectItem(dataDiv) {
244
- this.hideResultsList()
320
+ selectItem(dataDiv, force = false) {
321
+ const selectedValue = dataDiv.getAttribute("data-satis-dropdown-item-value") || ""
322
+ const selectedValueText = dataDiv.getAttribute("data-satis-dropdown-item-text") || ""
323
+ this.copyItemAttributes(dataDiv, this.hiddenSelectTarget) // FIXME: we are now supporting multiple values; is this needed? We copy the attributes to options
245
324
 
246
- // Copy over data attributes on the item div to the hidden input
247
- Array.prototype.slice.call(dataDiv.attributes).forEach((attr) => {
248
- if (attr.name.startsWith("data") && !attr.name.startsWith("data-satis") && !attr.name.startsWith("data-action")) {
249
- this.hiddenInputTarget.setAttribute(attr.name, attr.value)
325
+ const option = this.createOption({ text: selectedValueText, value: selectedValue })
326
+ this.copyItemAttributes(dataDiv, option)
327
+ const optionExists = Array.from(this.hiddenSelectTarget.options).some(
328
+ (opt) => opt.value === option.value && this.dataAttributesAreEqual(opt, option)
329
+ )
330
+
331
+ // we dont select items that already have been selected, open list
332
+ if (!force) {
333
+ if (optionExists) {
334
+ if (!this.resultsShown) this.showResultsList()
335
+ return
250
336
  }
251
- })
337
+ }
252
338
 
253
- this.searchInputTarget.value = dataDiv.getAttribute("data-satis-dropdown-item-text")
254
- this.hiddenInputTarget.value = dataDiv.getAttribute("data-satis-dropdown-item-value")
255
- this.lastSearch = this.searchInputTarget.value
339
+ // clear the search input if we are not in multi select mode
340
+ if (!this.isMultipleValue) {
341
+ this.lastServerRefreshOptions.clear()
342
+ this.hiddenSelectTarget.innerHTML = ""
343
+ this.selectedItemsTemplateTarget.innerHTML = ""
344
+ this.searchInputTarget.value = selectedValueText
345
+ } else
346
+ this.selectedItemsTemplateTarget.content.querySelector(`[data-satis-dropdown-item-value="${selectedValue}"]`)?.remove()
256
347
 
257
- this.hiddenInputTarget.dispatchEvent(new Event("change"))
348
+ this.hiddenSelectTarget.add(option)
349
+ this.lastServerRefreshOptions.add(selectedValue)
350
+ this.selectedItemsTemplateTarget.content.appendChild(dataDiv.cloneNode(true))
258
351
 
259
- if (this.searchInputTarget.closest(".bg-white").classList.contains("warning")) {
260
- this.searchInputTarget.closest(".bg-white").classList.remove("warning")
352
+ this.hiddenSelectTarget.dispatchEvent(new Event("change"))
353
+ this.setSelectedItem(selectedValue)
354
+ this.hideResultsList()
355
+ this.validateSearchQuery()
356
+ }
357
+
358
+ setHiddenSelect() {
359
+ if (this.hiddenSelectTarget.options.length === 0) {
360
+ this.searchInputTarget.value = ""
361
+ this.pillsTarget.innerHTML = ""
362
+ this.pillsTarget.classList.add("hidden")
363
+ return true
364
+ }
365
+
366
+ if (this.isMultipleValue) {
367
+ Array.from(this.hiddenSelectTarget.options).forEach((opt) => {
368
+ if (!opt.value) return
369
+ const pillExists = this.pillsTarget.querySelector(
370
+ `[data-satis-dropdown-target="pill"] > button[data-satis-dropdown-id-param="${opt.value}"]`
371
+ )
372
+ if (!pillExists) {
373
+ // Add pill to selection
374
+ const pillTemplate = this.pillTemplateTarget.content.firstElementChild.cloneNode(true)
375
+ pillTemplate.prepend(opt.text || opt.value)
376
+ pillTemplate.querySelector("button").setAttribute("data-satis-dropdown-id-param", opt.value)
377
+ this.pillsTarget.appendChild(pillTemplate)
378
+ }
379
+ })
380
+
381
+ this.searchInputTarget.value = ""
382
+ this.pillsTarget.classList.remove("hidden")
383
+ } else if (this.hiddenSelectTarget.options.length == 1) {
384
+ const opt = this.hiddenSelectTarget.options[0]
385
+ this.searchInputTarget.value = opt.text
261
386
  }
262
387
  }
263
388
 
264
389
  // --- Helpers
265
390
 
266
- setHiddenInput() {
267
- const currentItem = this.itemTargets.find((item) => {
268
- return this.hiddenInputTarget.value == item.getAttribute("data-satis-dropdown-item-value")
269
- })
270
- if (currentItem) {
271
- this.searchInputTarget.value = currentItem.getAttribute("data-satis-dropdown-item-text")
272
-
273
- Array.prototype.slice.call(currentItem.attributes).forEach((attr) => {
274
- if (
275
- attr.name.startsWith("data") &&
276
- !attr.name.startsWith("data-satis") &&
277
- !attr.name.startsWith("data-action")
278
- ) {
279
- this.hiddenInputTarget.setAttribute(attr.name, attr.value)
280
- }
281
- })
282
- if (!this.hiddenInputTarget.getAttribute("data-reflex")) {
283
- this.hiddenInputTarget.dispatchEvent(new CustomEvent("change", { detail: { src: "satis-dropdown" } }))
284
- }
285
- }
391
+ recordLastSearch() {
392
+ this.lastSearch = this.searchInputTarget.value ? this.searchInputTarget.value : ""
393
+ }
394
+
395
+ get searchQueryChanged() {
396
+ const searchQueryValue = this.filteredSearchQuery ? this.filteredSearchQuery : ""
397
+ const lastSearch = this.lastSearch ? this.lastSearch : ""
398
+ return searchQueryValue.length !== lastSearch.length ||
399
+ searchQueryValue.localeCompare(lastSearch, undefined, { sensitivity: "base" }) !== 0
400
+ }
401
+
402
+ removePill(event) {
403
+ event.preventDefault()
404
+
405
+ this.hiddenSelectTarget.removeChild(this.hiddenSelectTarget.querySelector(`option[value="${event.params.id}"]`))
406
+ this.lastServerRefreshOptions.delete(event.params.id)
407
+
408
+ this.pillTargets
409
+ .find((pill) => pill.querySelector("button").getAttribute("data-satis-dropdown-id-param") == event.params.id)
410
+ ?.remove()
411
+
412
+ //this.hiddenSelectTarget.dispatchEvent(new Event("change"))
286
413
  }
287
414
 
288
415
  toggleResultsList(event) {
@@ -293,14 +420,16 @@ export default class extends ApplicationController {
293
420
  // } else if (this.element.contains(document.activeElement)) {
294
421
  } else {
295
422
  this.filterResultsChainTo()
296
- if (this.hasResults) {
423
+
424
+ if(this.hasResults && !this.searchQueryChanged){
297
425
  this.showResultsList(event)
298
- } else {
299
- this.fetchResults(event)
426
+ }else {
427
+ if (this.hasUrlValue)
428
+ this.fetchResults(event)
429
+ else
430
+ this.localResults(event)
300
431
  }
301
432
  }
302
-
303
- event.preventDefault()
304
433
  return false
305
434
  }
306
435
 
@@ -308,15 +437,23 @@ export default class extends ApplicationController {
308
437
  this.resultsTarget.classList.remove("hidden")
309
438
  this.resultsTarget.setAttribute("data-show", "")
310
439
  this.popperInstance.update()
311
- this.toggleButtonTarget.querySelector(".fa-chevron-up").classList.remove("hidden")
312
- this.toggleButtonTarget.querySelector(".fa-chevron-down").classList.add("hidden")
440
+ if (this.hasToggleButtonTarget) {
441
+ this.toggleButtonTarget.querySelector(".fa-chevron-up").classList.remove("hidden")
442
+ this.toggleButtonTarget.querySelector(".fa-chevron-down").classList.add("hidden")
443
+ }
313
444
  }
314
445
 
315
446
  hideResultsList(event) {
316
447
  this.resultsTarget.classList.add("hidden")
317
448
  this.resultsTarget.removeAttribute("data-show")
318
- this.toggleButtonTarget.querySelector(".fa-chevron-up").classList.add("hidden")
319
- this.toggleButtonTarget.querySelector(".fa-chevron-down").classList.remove("hidden")
449
+ if (this.hasToggleButtonTarget) {
450
+ this.toggleButtonTarget.querySelector(".fa-chevron-up").classList.add("hidden")
451
+ this.toggleButtonTarget.querySelector(".fa-chevron-down").classList.remove("hidden")
452
+ }
453
+ }
454
+
455
+ getChainToElement() {
456
+ return this.hiddenSelectTarget?.form?.querySelector(`[name="${this.chainToValue}"]`)
320
457
  }
321
458
 
322
459
  filterResultsChainTo() {
@@ -325,11 +462,12 @@ export default class extends ApplicationController {
325
462
  }
326
463
 
327
464
  let chainToValue
328
- let chainTo = this.hiddenInputTarget.form.querySelector(`[name="${this.chainToValue}"]`)
465
+ let chainTo = this.getChainToElement()
329
466
  if (chainTo) {
330
467
  chainToValue = chainTo.value
331
468
  }
332
469
 
470
+ let listItems = 0
333
471
  this.itemTargets.forEach((item) => {
334
472
  let itemChainToValue = item.getAttribute("data-chain")
335
473
  let chainMatch = true
@@ -338,84 +476,125 @@ export default class extends ApplicationController {
338
476
  }
339
477
 
340
478
  if (chainMatch) {
479
+ listItems += 1
341
480
  item.classList.remove("hidden")
342
481
  } else {
343
482
  item.classList.add("hidden")
483
+ item.classList.remove("highlighted")
344
484
  }
345
485
  })
486
+ if (listItems == 1) {
487
+ this.selectItem(this.itemTargets.filter((item) => {
488
+ return item.classList != 'hidden'
489
+ })[0])
490
+ }
346
491
  }
347
492
 
348
493
  localResults(event) {
349
- if (this.searchInputTarget.value == this.lastSearch) {
350
- return
351
- }
352
-
353
- if (this.searchInputTarget.value.length < 2) {
494
+ if (!this.searchQueryChanged) {
495
+ if(!this.resultsShown) {
496
+ if (this.hasResults)
497
+ this.showResultsList(event)
498
+ else this.showSelectedItem()
499
+ }
500
+ this.validateSearchQuery()
354
501
  return
355
502
  }
356
503
 
357
- this.lastSearch = this.searchInputTarget.value
504
+ this.recordLastSearch()
358
505
 
506
+ // show all items again and count those that were already visible (previously matched)
507
+ let previouslyVisibleItemsCount = 0
359
508
  this.itemTargets.forEach((item) => {
360
- item.classList.remove("hidden")
361
- })
509
+ if (item.classList.contains('hidden')) {
510
+ item.classList.remove('hidden')
511
+ } else {
512
+ previouslyVisibleItemsCount++
513
+ }
514
+ });
362
515
 
363
516
  this.filterResultsChainTo()
364
517
 
518
+ // hide all items that don't match the search query
519
+ const searchValue = this.searchQueryValue
365
520
  let matches = []
366
521
  this.itemTargets.forEach((item) => {
367
- let text = item.getAttribute("data-satis-dropdown-item-text").toLowerCase()
368
- let value = item.getAttribute("data-satis-dropdown-item-value").toLowerCase()
369
-
370
- if (!item.classList.contains("hidden")) {
371
- if (this.needsExactMatchValue && text === this.searchInputTarget.value.toLowerCase()) {
372
- matches = matches.concat(item)
373
- } else if (!this.needsExactMatchValue && text.indexOf(this.searchInputTarget.value.toLowerCase()) >= 0) {
374
- matches = matches.concat(item)
522
+ const text = item.getAttribute("data-satis-dropdown-item-text")
523
+ const matched = this.needsExactMatchValue ?
524
+ searchValue.localeCompare(text, undefined, {sensitivity: 'base'}) === 0:
525
+ new RegExp(searchValue, "i").test(text)
526
+
527
+ const isHidden = item.classList.contains("hidden")
528
+ if (!isHidden) {
529
+ if (matched) {
530
+ matches.push(item)
375
531
  } else {
376
- item.classList.add("hidden")
532
+ item.classList.toggle("hidden", true)
377
533
  }
378
534
  }
379
535
  })
380
536
 
381
- if (this.freeTextValue && matches.length != 1) {
382
- this.hiddenInputTarget.value = this.lastSearch
383
- }
384
-
385
- if (
386
- matches.length == 1 &&
387
- matches[0].getAttribute("data-satis-dropdown-item-text").toLowerCase().indexOf(this.lastSearch.toLowerCase()) >= 0
388
- ) {
389
- this.selectItem(matches[0].closest('[data-satis-dropdown-target="item"]'))
390
- } else if (matches.length > 1) {
537
+ // don't show results
538
+ if (matches.length > 0) {
391
539
  this.showResultsList(event)
540
+ } else {
541
+ if (!this.showSelectedItem())
542
+ this.hideResultsList(event)
543
+ }
544
+
545
+ // auto select if there is only one match and we are not in freetext mode
546
+ if(!this.freeTextValue) {
547
+ if (matches.length === 1) {
548
+ if (this.filteredSearchQuery.length >= this.minSearchQueryLength &&
549
+ matches[0].getAttribute("data-satis-dropdown-item-text").toLowerCase().indexOf(this.lastSearch.toLowerCase()) >= 0) {
550
+ const dataDiv = matches[0].closest('[data-satis-dropdown-target="item"]')
551
+ this.selectItem(dataDiv)
552
+ this.setSelectedItem(dataDiv.getAttribute("data-satis-dropdown-item-value"))
553
+ this.searchQueryValue = ""
554
+ } else {
555
+ this.showSelectedItem()
556
+ }
557
+ // the selected item if there was only 1 item visible before
558
+ } else if(previouslyVisibleItemsCount === 1 && matches.length > 1) {
559
+ this.setSelectedItem()
560
+ }
392
561
  }
562
+
563
+ this.validateSearchQuery()
393
564
  }
394
565
 
395
566
  // Remote search
396
567
  fetchResults(event) {
397
568
  const promise = new Promise((resolve, reject) => {
398
569
  if (
399
- (this.searchInputTarget.value == this.lastSearch &&
570
+ (!this.searchQueryChanged &&
400
571
  (this.currentPage == this.lastPage || this.currentPage == this.endPage)) ||
401
572
  !this.hasUrlValue
402
573
  ) {
574
+ if(!this.resultsShown) {
575
+ if (this.hasResults)
576
+ this.showResultsList(event)
577
+ else this.showSelectedItem()
578
+ }
403
579
  return
404
580
  }
405
581
 
406
- if (this.searchInputTarget.value != this.lastSearch) {
582
+ if (this.searchQueryChanged) {
407
583
  this.currentPage = 1
408
584
  this.endPage = null
585
+ this.recordLastSearch()
409
586
  }
410
587
 
411
- this.lastSearch = this.searchInputTarget.value
412
588
  this.lastPage = this.currentPage
413
589
 
414
- let ourUrl = this.normalizedUrl
590
+ let ourUrl = this.normalizedUrl()
415
591
  let pageSize = this.pageSizeValue
416
- if (this.searchInputTarget.value.length >= 2) {
417
- ourUrl.searchParams.append("term", this.searchInputTarget.value)
592
+
593
+ if (event != null && (this.filteredSearchQuery >= 2 || this.lastSearch)) {
594
+ ourUrl.searchParams.append("term", this.searchQueryValue)
418
595
  }
596
+
597
+
419
598
  ourUrl.searchParams.append("page", this.currentPage)
420
599
  ourUrl.searchParams.append("page_size", pageSize)
421
600
  if (this.needsExactMatchValue) {
@@ -425,30 +604,34 @@ export default class extends ApplicationController {
425
604
  this.fetchResultsWith(ourUrl).then((itemCount) => {
426
605
  if (this.hasResults) {
427
606
  this.filterResultsChainTo()
428
- this.highLightSelected()
429
- this.showResultsList()
430
-
431
- if (
432
- this.nrOfItems == 1 &&
433
- this.itemTargets[0]
434
- .getAttribute("data-satis-dropdown-item-text")
435
- .toLowerCase()
436
- .indexOf(this.searchInputTarget.value.toLowerCase()) >= 0
437
- ) {
438
- this.selectItem(this.itemTargets[0].closest('[data-satis-dropdown-target="item"]'))
439
- } else if (this.nrOfItems == 1) {
440
- this.moveDown()
607
+
608
+ if (!this.resultsShown && !this.chainToValue) {
609
+ this.showResultsList()
610
+ }
611
+
612
+ // auto select when there is only 1 value
613
+ if (this.filteredSearchQuery.length >= this.minSearchQueryLength && this.nrOfItems === 1 && !this.freeTextValue) {
614
+ const dataDiv = this.itemTargets[0].closest('[data-satis-dropdown-target="item"]')
615
+ this.selectItem(dataDiv)
616
+ this.setSelectedItem(dataDiv.getAttribute("data-satis-dropdown-item-value"))
617
+ this.searchQueryValue = ""
441
618
  }
442
619
 
443
620
  if (itemCount > 0) {
444
621
  this.currentPage += 1
445
622
  }
446
- if (itemCount < pageSize) {
623
+
624
+ // when the count < page_size we assume we reached the end of the list (count can be 0)
625
+ if (itemCount != pageSize) {
447
626
  this.endPage = this.currentPage
448
627
  }
449
628
 
450
629
  resolve()
630
+ } else {
631
+ this.showSelectedItem()
451
632
  }
633
+
634
+ this.validateSearchQuery()
452
635
  })
453
636
  })
454
637
  return promise
@@ -482,7 +665,95 @@ export default class extends ApplicationController {
482
665
  return promise
483
666
  }
484
667
 
485
- get normalizedUrl() {
668
+ get filteredSearchQuery(){
669
+ if(this.searchQueryValue < this.minSearchQueryLength) return ""
670
+ return this.searchQueryValue
671
+ }
672
+
673
+ get selectionChangedSinceLastRefresh() {
674
+ return (
675
+ this.hiddenSelectTarget.options.length !== this.lastServerRefreshOptions.size ||
676
+ !Array.from(this.hiddenSelectTarget.options).every((option) => this.lastServerRefreshOptions.has(option.value))
677
+ )
678
+ }
679
+
680
+ refreshSelectionFromServer() {
681
+ if (!this.selectionChangedSinceLastRefresh) return Promise.resolve()
682
+
683
+ let updated = 0
684
+ Array.from(this.hiddenSelectTarget.options).forEach((opt) => {
685
+ // try to find the items locally
686
+ let item = this.itemsTarget.querySelector('[data-satis-dropdown-item-value="' + opt.value + '"]')
687
+ if (item) {
688
+ opt.text = item.getAttribute("data-satis-dropdown-item-text")
689
+
690
+ // Copy over data attributes on the item div to the option
691
+ this.copyItemAttributes(item, opt)
692
+ this.selectedItemsTemplateTarget.content.appendChild(item.cloneNode(true))
693
+ updated++
694
+ }
695
+
696
+ this.lastServerRefreshOptions.add(opt.value)
697
+ })
698
+
699
+ if (!this.hasUrlValue || this.hiddenSelectTarget.options.length === updated) return Promise.resolve()
700
+
701
+ const promise = new Promise((resolve, reject) => {
702
+ if (!this.hasUrlValue) return
703
+
704
+ const ourUrl = this.normalizedUrl()
705
+
706
+ let selectedIds = Array.from(this.hiddenSelectTarget.options).map((opt) => opt.value)
707
+
708
+ // make sure we get all selected items
709
+ //ourUrl.searchParams.append("page", 1)
710
+ //ourUrl.searchParams.append("page_size", selectedIds.length)
711
+ // parameters with [] will be converted to an array
712
+ if (selectedIds.length > 0)
713
+ selectedIds.forEach((id) => ourUrl.searchParams.append(selectedIds.length === 1 ? "id" : "id[]", id))
714
+
715
+ fetch(ourUrl.href, {}).then((response) => {
716
+ if (response.ok)
717
+ response.text().then((data) => {
718
+ let tmpDiv = document.createElement("div")
719
+ tmpDiv.innerHTML = data
720
+
721
+ for (let i = 0; i < this.hiddenSelectTarget.options.length; i++) {
722
+ let opt = this.hiddenSelectTarget.options[i]
723
+ let item = tmpDiv.querySelector('[data-satis-dropdown-item-value="' + opt.value + '"]')
724
+ if (!item && !this.freeTextValue) {
725
+ this.selectedItemsTemplateTarget.content.querySelector(`[data-satis-dropdown-item-value="${opt.value}"]`)?.remove()
726
+ opt.remove()
727
+ this.lastServerRefreshOptions.delete(opt.value)
728
+ } else {
729
+ let text = item.getAttribute("data-satis-dropdown-item-text")
730
+
731
+ if (opt.text != text) {
732
+ if (text === "") opt.text = opt.id
733
+ else opt.text = text
734
+ }
735
+
736
+ // Copy over data attributes on the item div to the option
737
+ this.copyItemAttributes(item, opt)
738
+ this.selectedItemsTemplateTarget.content.appendChild(item.cloneNode(true))
739
+ }
740
+ }
741
+
742
+ // blank option
743
+ if (this.hiddenSelectTarget.options.length === 0) {
744
+ let option = this.createOption()
745
+ this.hiddenSelectTarget.options.add(option)
746
+ this.lastServerRefreshOptions.add(option.value)
747
+ }
748
+
749
+ resolve()
750
+ })
751
+ })
752
+ })
753
+ return promise
754
+ }
755
+
756
+ normalizedUrl() {
486
757
  let ourUrl
487
758
  try {
488
759
  ourUrl = new URL(this.urlValue)
@@ -494,6 +765,7 @@ export default class extends ApplicationController {
494
765
  const form = this.element.closest("form")
495
766
  Object.entries(this.urlParamsValue).forEach((item) => {
496
767
  let elm = form.querySelector(`[name='${item[1]}']`)
768
+
497
769
  if (elm) {
498
770
  ourUrl.searchParams.append(item[0], elm.value)
499
771
  } else {
@@ -501,6 +773,14 @@ export default class extends ApplicationController {
501
773
  }
502
774
  })
503
775
 
776
+ let chainTo = this.getChainToElement()
777
+ if (chainTo) {
778
+ let chainToParam = chainTo
779
+ .getAttribute("name")
780
+ .substring(chainTo.getAttribute("name").lastIndexOf("[") + 1, chainTo.getAttribute("name").lastIndexOf("]"))
781
+ ourUrl.searchParams.append(chainToParam, chainTo.value)
782
+ }
783
+
504
784
  return ourUrl
505
785
  }
506
786
 
@@ -533,24 +813,42 @@ export default class extends ApplicationController {
533
813
  }
534
814
 
535
815
  get selectedItem() {
816
+ if (this.selectedIndex === -1) return
536
817
  return this.itemTargets.filter((item) => {
537
818
  return !item.classList.contains("hidden")
538
819
  })[this.selectedIndex]
539
820
  }
540
821
 
541
822
  lowLightSelected() {
542
- if (this.selectedItem) {
543
- this.selectedItem.classList.remove("bg-primary-200")
544
- }
823
+ this.itemsTarget.querySelectorAll('.highlighted[data-satis-dropdown-target="item"]').forEach((item) => {
824
+ item.classList.toggle("highlighted")
825
+ })
545
826
  }
546
827
 
547
828
  highLightSelected() {
548
- if (this.selectedItem) {
549
- this.selectedItem.classList.add("bg-primary-200")
550
- this.selectedItem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" })
829
+ const selectedItem = this.selectedItem
830
+ if (selectedItem) {
831
+ selectedItem.classList.toggle("highlighted", true)
832
+ selectedItem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" })
551
833
  }
552
834
  }
553
835
 
836
+ /*
837
+ * Set the selected item base on an the items 'data-satis-dropdown-item-value' attribute
838
+ * @param {string} value - The value to match the item against
839
+ */
840
+ setSelectedItem(value) {
841
+ this.lowLightSelected()
842
+ if (!value) {
843
+ this.selectedIndex = -1
844
+ return
845
+ }
846
+ const itemTargets = this.itemTargets;
847
+ const visibleItems = itemTargets.filter(item => !item.classList.contains("hidden"));
848
+ this.selectedIndex = visibleItems.findIndex(item => item.getAttribute("data-satis-dropdown-item-value") === value);
849
+ this.highLightSelected()
850
+ }
851
+
554
852
  moveDown() {
555
853
  this.lowLightSelected()
556
854
  this.increaseSelectedIndex()
@@ -562,9 +860,33 @@ export default class extends ApplicationController {
562
860
  this.decreaseSelectedIndex()
563
861
  this.highLightSelected()
564
862
  }
863
+ validateSearchQuery() {
864
+ const trimmedValue = this.searchInputTarget.value.trim();
865
+ const elements = this.selectedItemsTemplateTarget.content.querySelectorAll(`[data-satis-dropdown-item-text*="${trimmedValue}"]`);
866
+ const selected = Array.from(elements).find(element => element.getAttribute('data-satis-dropdown-item-text').trim() === trimmedValue);
867
+ if (!selected && this.searchInputTarget.value.length > 0 && !this.freeTextValue) {
868
+ this.searchInputTarget.closest(".bg-white").classList.toggle("warning", true)
869
+ } else {
870
+ this.searchInputTarget.closest(".bg-white").classList.toggle("warning", false)
871
+ }
872
+ }
565
873
 
874
+ // clear search input and hide results
566
875
  resetSearchInput(event) {
567
- this.setHiddenInput()
876
+ if (this.multiSelectValue) {
877
+ this.searchInputTarget.value = ""
878
+ } else {
879
+ if (this.hiddenSelectTarget.options.length > 0) {
880
+ const option = this.hiddenSelectTarget.options[0]
881
+ this.searchInputTarget.value = option.text
882
+ }
883
+ }
884
+
885
+ if (this.resultsShown) {
886
+ this.hideResultsList(event)
887
+ }
888
+
889
+ this.validateSearchQuery()
568
890
  }
569
891
 
570
892
  clickedOutside(event) {
@@ -577,4 +899,78 @@ export default class extends ApplicationController {
577
899
  }
578
900
  }
579
901
  }
902
+
903
+ copyItemAttributes(item, dest) {
904
+ Array.prototype.slice.call(item.attributes).forEach((attr) => {
905
+ if (attr.name.startsWith("data") && !attr.name.startsWith("data-satis") && !attr.name.startsWith("data-action")) {
906
+ dest.setAttribute(attr.name, attr.value)
907
+ }
908
+ })
909
+ }
910
+
911
+ createOption(options) {
912
+ options = Object.assign({ text: "", value: "", selected: true }, options)
913
+
914
+ let option = document.createElement("option")
915
+ option.text = options.text
916
+ option.value = options.value
917
+ option.setAttribute("selected", options.selected)
918
+ return option
919
+ }
920
+
921
+ dataAttributesAreEqual(el1, el2) {
922
+ const keys1 = Object.keys(el1.dataset)
923
+ const keys2 = Object.keys(el2.dataset)
924
+ if (keys1.length !== keys2.length) return false
925
+
926
+ for (const key of keys1) {
927
+ if (el1.dataset[key] !== el2.dataset[key]) {
928
+ return false
929
+ }
930
+ }
931
+ return true
932
+ }
933
+
934
+ get hasFocus() {
935
+ const activeElement = document.activeElement;
936
+ if (activeElement === this.element ||
937
+ this.element.contains(activeElement) ||
938
+ this.element.querySelector(':focus') !== null) {
939
+ return true;
940
+ }
941
+ return false;
942
+ }
943
+
944
+ // Selected items are being cached in selectItemsTemplate. Sometimes we want to show the selected item in the results list
945
+ // As the selected item may not be in the results list, we cache the item in the template and re-add it when needed.
946
+ showSelectedItem() {
947
+ if (this.isMultipleValue
948
+ || this.freeTextValue
949
+ || this.hiddenSelectTarget.options.length === 0
950
+ || !this.hasFocus
951
+ ) return false;
952
+
953
+ const option = this.hiddenSelectTarget.options[0]
954
+ let item = this.itemsTarget.querySelector(`[data-satis-dropdown-item-value="${option.value}"]`)
955
+ if (item) {
956
+ item.classList.remove("hidden")
957
+ } else {
958
+ item = this.selectedItemsTemplateTarget.content.querySelector(`[data-satis-dropdown-item-value="${option.value}"]`)
959
+ if (item) {
960
+ item = item.cloneNode(true)
961
+ item.classList.remove("hidden")
962
+ item.setAttribute("data-satis-dropdown-target", "item")
963
+ item.setAttribute("data-action", "click->satis-dropdown#select")
964
+ this.itemsTarget.appendChild(item)
965
+ }
966
+ }
967
+
968
+ if(item) {
969
+ if (!this.resultsShown)
970
+ this.showResultsList()
971
+ this.setSelectedItem(option.value)
972
+ }
973
+
974
+ return item != null;
975
+ }
580
976
  }