@aquera/nile-elements 1.5.4 → 1.5.6

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 (297) hide show
  1. package/README.md +10 -0
  2. package/demo/index.css +9 -0
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.esm.js +1 -1
  5. package/dist/index.js +871 -255
  6. package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js +1 -1
  7. package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js.map +1 -1
  8. package/dist/nile-auto-complete/nile-auto-complete.css.esm.js +13 -2
  9. package/dist/nile-button/nile-button.cjs.js +1 -1
  10. package/dist/nile-button/nile-button.cjs.js.map +1 -1
  11. package/dist/nile-button/nile-button.css.cjs.js +1 -1
  12. package/dist/nile-button/nile-button.css.cjs.js.map +1 -1
  13. package/dist/nile-button/nile-button.css.esm.js +89 -33
  14. package/dist/nile-button/nile-button.esm.js +2 -2
  15. package/dist/nile-calendar/nile-calendar.cjs.js +1 -1
  16. package/dist/nile-calendar/nile-calendar.cjs.js.map +1 -1
  17. package/dist/nile-calendar/nile-calendar.css.cjs.js +1 -1
  18. package/dist/nile-calendar/nile-calendar.css.cjs.js.map +1 -1
  19. package/dist/nile-calendar/nile-calendar.css.esm.js +59 -2
  20. package/dist/nile-calendar/nile-calendar.esm.js +16 -10
  21. package/dist/nile-checkbox/nile-checkbox.css.cjs.js +1 -1
  22. package/dist/nile-checkbox/nile-checkbox.css.cjs.js.map +1 -1
  23. package/dist/nile-checkbox/nile-checkbox.css.esm.js +4 -5
  24. package/dist/nile-chip/nile-chip.css.cjs.js +1 -1
  25. package/dist/nile-chip/nile-chip.css.cjs.js.map +1 -1
  26. package/dist/nile-chip/nile-chip.css.esm.js +37 -23
  27. package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
  28. package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
  29. package/dist/nile-code-editor/nile-code-editor.css.cjs.js +1 -1
  30. package/dist/nile-code-editor/nile-code-editor.css.cjs.js.map +1 -1
  31. package/dist/nile-code-editor/nile-code-editor.css.esm.js +7 -6
  32. package/dist/nile-code-editor/nile-code-editor.esm.js +1 -1
  33. package/dist/nile-date-picker/nile-date-picker.cjs.js +1 -1
  34. package/dist/nile-date-picker/nile-date-picker.cjs.js.map +1 -1
  35. package/dist/nile-date-picker/nile-date-picker.esm.js +4 -4
  36. package/dist/nile-detail/index.cjs.js +2 -0
  37. package/dist/nile-detail/index.cjs.js.map +1 -0
  38. package/dist/nile-detail/index.esm.js +1 -0
  39. package/dist/nile-detail/nile-detail.cjs.js +2 -0
  40. package/dist/nile-detail/nile-detail.cjs.js.map +1 -0
  41. package/dist/nile-detail/nile-detail.css.cjs.js +2 -0
  42. package/dist/nile-detail/nile-detail.css.cjs.js.map +1 -0
  43. package/dist/nile-detail/nile-detail.css.esm.js +149 -0
  44. package/dist/nile-detail/nile-detail.esm.js +45 -0
  45. package/dist/nile-detail/nile-detail.utils.cjs.js +2 -0
  46. package/dist/nile-detail/nile-detail.utils.cjs.js.map +1 -0
  47. package/dist/nile-detail/nile-detail.utils.esm.js +1 -0
  48. package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
  49. package/dist/nile-dropdown/nile-dropdown.cjs.js.map +1 -1
  50. package/dist/nile-dropdown/nile-dropdown.esm.js +1 -1
  51. package/dist/nile-file-upload/utils/file-validation.util.cjs.js +2 -2
  52. package/dist/nile-file-upload/utils/file-validation.util.cjs.js.map +1 -1
  53. package/dist/nile-file-upload/utils/file-validation.util.esm.js +1 -1
  54. package/dist/nile-filter-chip/nile-filter-chip.cjs.js +1 -1
  55. package/dist/nile-filter-chip/nile-filter-chip.cjs.js.map +1 -1
  56. package/dist/nile-filter-chip/nile-filter-chip.css.cjs.js +1 -1
  57. package/dist/nile-filter-chip/nile-filter-chip.css.cjs.js.map +1 -1
  58. package/dist/nile-filter-chip/nile-filter-chip.css.esm.js +24 -2
  59. package/dist/nile-filter-chip/nile-filter-chip.esm.js +2 -2
  60. package/dist/nile-inline-edit/nile-inline-edit.cjs.js +1 -1
  61. package/dist/nile-inline-edit/nile-inline-edit.cjs.js.map +1 -1
  62. package/dist/nile-inline-edit/nile-inline-edit.css.cjs.js +1 -1
  63. package/dist/nile-inline-edit/nile-inline-edit.css.cjs.js.map +1 -1
  64. package/dist/nile-inline-edit/nile-inline-edit.css.esm.js +15 -6
  65. package/dist/nile-inline-edit/nile-inline-edit.esm.js +2 -2
  66. package/dist/nile-inline-sidebar/nile-inline-sidebar.cjs.js +1 -1
  67. package/dist/nile-inline-sidebar/nile-inline-sidebar.cjs.js.map +1 -1
  68. package/dist/nile-inline-sidebar/nile-inline-sidebar.css.cjs.js +1 -1
  69. package/dist/nile-inline-sidebar/nile-inline-sidebar.css.cjs.js.map +1 -1
  70. package/dist/nile-inline-sidebar/nile-inline-sidebar.css.esm.js +19 -0
  71. package/dist/nile-inline-sidebar/nile-inline-sidebar.esm.js +16 -14
  72. package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.cjs.js +1 -1
  73. package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.cjs.js.map +1 -1
  74. package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.esm.js +8 -3
  75. package/dist/nile-input/nile-input.css.cjs.js +1 -1
  76. package/dist/nile-input/nile-input.css.cjs.js.map +1 -1
  77. package/dist/nile-input/nile-input.css.esm.js +4 -4
  78. package/dist/nile-link/nile-link.cjs.js +1 -1
  79. package/dist/nile-link/nile-link.cjs.js.map +1 -1
  80. package/dist/nile-link/nile-link.css.cjs.js +1 -1
  81. package/dist/nile-link/nile-link.css.cjs.js.map +1 -1
  82. package/dist/nile-link/nile-link.css.esm.js +3 -4
  83. package/dist/nile-link/nile-link.esm.js +2 -2
  84. package/dist/nile-pagination/nile-pagination.cjs.js +1 -1
  85. package/dist/nile-pagination/nile-pagination.cjs.js.map +1 -1
  86. package/dist/nile-pagination/nile-pagination.css.cjs.js +1 -1
  87. package/dist/nile-pagination/nile-pagination.css.cjs.js.map +1 -1
  88. package/dist/nile-pagination/nile-pagination.css.esm.js +109 -5
  89. package/dist/nile-pagination/nile-pagination.esm.js +70 -3
  90. package/dist/nile-qr-code/index.cjs.js +2 -0
  91. package/dist/nile-qr-code/index.cjs.js.map +1 -0
  92. package/dist/nile-qr-code/index.esm.js +1 -0
  93. package/dist/nile-qr-code/nile-qr-code-utils.cjs.js +2 -0
  94. package/dist/nile-qr-code/nile-qr-code-utils.cjs.js.map +1 -0
  95. package/dist/nile-qr-code/nile-qr-code-utils.esm.js +1 -0
  96. package/dist/nile-qr-code/nile-qr-code.cjs.js +2 -0
  97. package/dist/nile-qr-code/nile-qr-code.cjs.js.map +1 -0
  98. package/dist/nile-qr-code/nile-qr-code.css.cjs.js +2 -0
  99. package/dist/nile-qr-code/nile-qr-code.css.cjs.js.map +1 -0
  100. package/dist/nile-qr-code/nile-qr-code.css.esm.js +12 -0
  101. package/dist/nile-qr-code/nile-qr-code.esm.js +9 -0
  102. package/dist/nile-radio/nile-radio.css.cjs.js +1 -1
  103. package/dist/nile-radio/nile-radio.css.cjs.js.map +1 -1
  104. package/dist/nile-radio/nile-radio.css.esm.js +1 -1
  105. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
  106. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
  107. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
  108. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
  109. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +25 -6
  110. package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
  111. package/dist/nile-rich-text-editor/nile-rte-select.cjs.js +1 -1
  112. package/dist/nile-rich-text-editor/nile-rte-select.cjs.js.map +1 -1
  113. package/dist/nile-rich-text-editor/nile-rte-select.esm.js +4 -3
  114. package/dist/nile-select/nile-select.css.cjs.js +1 -1
  115. package/dist/nile-select/nile-select.css.cjs.js.map +1 -1
  116. package/dist/nile-select/nile-select.css.esm.js +7 -4
  117. package/dist/nile-slide-toggle/nile-slide-toggle.css.cjs.js +1 -1
  118. package/dist/nile-slide-toggle/nile-slide-toggle.css.cjs.js.map +1 -1
  119. package/dist/nile-slide-toggle/nile-slide-toggle.css.esm.js +5 -1
  120. package/dist/nile-textarea/nile-textarea.css.cjs.js +1 -1
  121. package/dist/nile-textarea/nile-textarea.css.cjs.js.map +1 -1
  122. package/dist/nile-textarea/nile-textarea.css.esm.js +10 -5
  123. package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js +1 -1
  124. package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js.map +1 -1
  125. package/dist/nile-virtual-select/nile-virtual-select.css.esm.js +5 -3
  126. package/dist/src/index.d.ts +2 -0
  127. package/dist/src/index.js +2 -0
  128. package/dist/src/index.js.map +1 -1
  129. package/dist/src/nile-auto-complete/nile-auto-complete.css.js +11 -0
  130. package/dist/src/nile-auto-complete/nile-auto-complete.css.js.map +1 -1
  131. package/dist/src/nile-button/nile-button.css.js +89 -33
  132. package/dist/src/nile-button/nile-button.css.js.map +1 -1
  133. package/dist/src/nile-button/nile-button.js +5 -5
  134. package/dist/src/nile-button/nile-button.js.map +1 -1
  135. package/dist/src/nile-calendar/nile-calendar.css.js +57 -0
  136. package/dist/src/nile-calendar/nile-calendar.css.js.map +1 -1
  137. package/dist/src/nile-calendar/nile-calendar.d.ts +1 -0
  138. package/dist/src/nile-calendar/nile-calendar.js +24 -6
  139. package/dist/src/nile-calendar/nile-calendar.js.map +1 -1
  140. package/dist/src/nile-checkbox/nile-checkbox.css.js +4 -5
  141. package/dist/src/nile-checkbox/nile-checkbox.css.js.map +1 -1
  142. package/dist/src/nile-chip/nile-chip.css.js +35 -21
  143. package/dist/src/nile-chip/nile-chip.css.js.map +1 -1
  144. package/dist/src/nile-code-editor/nile-code-editor.css.js +7 -6
  145. package/dist/src/nile-code-editor/nile-code-editor.css.js.map +1 -1
  146. package/dist/src/nile-code-editor/nile-code-editor.d.ts +1 -1
  147. package/dist/src/nile-code-editor/nile-code-editor.js +7 -1
  148. package/dist/src/nile-code-editor/nile-code-editor.js.map +1 -1
  149. package/dist/src/nile-date-picker/nile-date-picker.d.ts +3 -0
  150. package/dist/src/nile-date-picker/nile-date-picker.js +22 -2
  151. package/dist/src/nile-date-picker/nile-date-picker.js.map +1 -1
  152. package/dist/src/nile-detail/index.d.ts +1 -0
  153. package/dist/src/nile-detail/index.js +2 -0
  154. package/dist/src/nile-detail/index.js.map +1 -0
  155. package/dist/src/nile-detail/nile-detail.css.d.ts +3 -0
  156. package/dist/src/nile-detail/nile-detail.css.js +152 -0
  157. package/dist/src/nile-detail/nile-detail.css.js.map +1 -0
  158. package/dist/src/nile-detail/nile-detail.d.ts +29 -0
  159. package/dist/src/nile-detail/nile-detail.js +143 -0
  160. package/dist/src/nile-detail/nile-detail.js.map +1 -0
  161. package/dist/src/nile-detail/nile-detail.test.d.ts +1 -0
  162. package/dist/src/nile-detail/nile-detail.test.js +168 -0
  163. package/dist/src/nile-detail/nile-detail.test.js.map +1 -0
  164. package/dist/src/nile-detail/nile-detail.utils.d.ts +8 -0
  165. package/dist/src/nile-detail/nile-detail.utils.js +117 -0
  166. package/dist/src/nile-detail/nile-detail.utils.js.map +1 -0
  167. package/dist/src/nile-dropdown/nile-dropdown.d.ts +1 -0
  168. package/dist/src/nile-dropdown/nile-dropdown.js +11 -0
  169. package/dist/src/nile-dropdown/nile-dropdown.js.map +1 -1
  170. package/dist/src/nile-file-upload/utils/file-validation.util.js +11 -6
  171. package/dist/src/nile-file-upload/utils/file-validation.util.js.map +1 -1
  172. package/dist/src/nile-filter-chip/nile-filter-chip.css.js +22 -0
  173. package/dist/src/nile-filter-chip/nile-filter-chip.css.js.map +1 -1
  174. package/dist/src/nile-filter-chip/nile-filter-chip.d.ts +1 -0
  175. package/dist/src/nile-filter-chip/nile-filter-chip.js +6 -0
  176. package/dist/src/nile-filter-chip/nile-filter-chip.js.map +1 -1
  177. package/dist/src/nile-inline-edit/nile-inline-edit.css.js +15 -6
  178. package/dist/src/nile-inline-edit/nile-inline-edit.css.js.map +1 -1
  179. package/dist/src/nile-inline-edit/nile-inline-edit.d.ts +2 -0
  180. package/dist/src/nile-inline-edit/nile-inline-edit.js +7 -0
  181. package/dist/src/nile-inline-edit/nile-inline-edit.js.map +1 -1
  182. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.css.js +19 -0
  183. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.css.js.map +1 -1
  184. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.d.ts +6 -0
  185. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.js +88 -4
  186. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.js.map +1 -1
  187. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.d.ts +3 -0
  188. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.js +110 -0
  189. package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.js.map +1 -0
  190. package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.d.ts +2 -0
  191. package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.js +109 -0
  192. package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.js.map +1 -0
  193. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.d.ts +4 -0
  194. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.js +30 -2
  195. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.js.map +1 -1
  196. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.d.ts +2 -0
  197. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.js +109 -0
  198. package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.js.map +1 -0
  199. package/dist/src/nile-input/nile-input.css.js +4 -4
  200. package/dist/src/nile-input/nile-input.css.js.map +1 -1
  201. package/dist/src/nile-link/nile-link.css.js +1 -2
  202. package/dist/src/nile-link/nile-link.css.js.map +1 -1
  203. package/dist/src/nile-link/nile-link.js +1 -0
  204. package/dist/src/nile-link/nile-link.js.map +1 -1
  205. package/dist/src/nile-pagination/nile-pagination.css.js +107 -3
  206. package/dist/src/nile-pagination/nile-pagination.css.js.map +1 -1
  207. package/dist/src/nile-pagination/nile-pagination.d.ts +5 -1
  208. package/dist/src/nile-pagination/nile-pagination.js +84 -1
  209. package/dist/src/nile-pagination/nile-pagination.js.map +1 -1
  210. package/dist/src/nile-pagination/nile-pagination.test.js +1187 -103
  211. package/dist/src/nile-pagination/nile-pagination.test.js.map +1 -1
  212. package/dist/src/nile-qr-code/index.d.ts +1 -0
  213. package/dist/src/nile-qr-code/index.js +2 -0
  214. package/dist/src/nile-qr-code/index.js.map +1 -0
  215. package/dist/src/nile-qr-code/nile-qr-code-utils.d.ts +15 -0
  216. package/dist/src/nile-qr-code/nile-qr-code-utils.js +678 -0
  217. package/dist/src/nile-qr-code/nile-qr-code-utils.js.map +1 -0
  218. package/dist/src/nile-qr-code/nile-qr-code.css.d.ts +12 -0
  219. package/dist/src/nile-qr-code/nile-qr-code.css.js +24 -0
  220. package/dist/src/nile-qr-code/nile-qr-code.css.js.map +1 -0
  221. package/dist/src/nile-qr-code/nile-qr-code.d.ts +127 -0
  222. package/dist/src/nile-qr-code/nile-qr-code.js +381 -0
  223. package/dist/src/nile-qr-code/nile-qr-code.js.map +1 -0
  224. package/dist/src/nile-qr-code/nile-qr-code.test.d.ts +1 -0
  225. package/dist/src/nile-qr-code/nile-qr-code.test.js +719 -0
  226. package/dist/src/nile-qr-code/nile-qr-code.test.js.map +1 -0
  227. package/dist/src/nile-radio/nile-radio.css.js +1 -1
  228. package/dist/src/nile-radio/nile-radio.css.js.map +1 -1
  229. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +25 -6
  230. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
  231. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +1 -0
  232. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +17 -6
  233. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
  234. package/dist/src/nile-rich-text-editor/nile-rte-select.d.ts +1 -0
  235. package/dist/src/nile-rich-text-editor/nile-rte-select.js +7 -2
  236. package/dist/src/nile-rich-text-editor/nile-rte-select.js.map +1 -1
  237. package/dist/src/nile-select/nile-select.css.js +7 -4
  238. package/dist/src/nile-select/nile-select.css.js.map +1 -1
  239. package/dist/src/nile-slide-toggle/nile-slide-toggle.css.js +5 -1
  240. package/dist/src/nile-slide-toggle/nile-slide-toggle.css.js.map +1 -1
  241. package/dist/src/nile-textarea/nile-textarea.css.js +10 -5
  242. package/dist/src/nile-textarea/nile-textarea.css.js.map +1 -1
  243. package/dist/src/nile-virtual-select/nile-virtual-select.css.js +5 -3
  244. package/dist/src/nile-virtual-select/nile-virtual-select.css.js.map +1 -1
  245. package/dist/src/version.js +1 -1
  246. package/dist/src/version.js.map +1 -1
  247. package/dist/tsconfig.tsbuildinfo +1 -1
  248. package/package.json +3 -2
  249. package/src/index.ts +3 -2
  250. package/src/nile-auto-complete/nile-auto-complete.css.ts +11 -0
  251. package/src/nile-button/nile-button.css.ts +89 -33
  252. package/src/nile-button/nile-button.ts +6 -5
  253. package/src/nile-calendar/nile-calendar.css.ts +57 -0
  254. package/src/nile-calendar/nile-calendar.ts +17 -6
  255. package/src/nile-checkbox/nile-checkbox.css.ts +4 -5
  256. package/src/nile-chip/nile-chip.css.ts +35 -21
  257. package/src/nile-code-editor/nile-code-editor.css.ts +7 -6
  258. package/src/nile-code-editor/nile-code-editor.ts +7 -1
  259. package/src/nile-date-picker/nile-date-picker.ts +22 -2
  260. package/src/nile-detail/index.ts +1 -0
  261. package/src/nile-detail/nile-detail.css.ts +153 -0
  262. package/src/nile-detail/nile-detail.test.ts +215 -0
  263. package/src/nile-detail/nile-detail.ts +140 -0
  264. package/src/nile-detail/nile-detail.utils.ts +133 -0
  265. package/src/nile-dropdown/nile-dropdown.ts +11 -0
  266. package/src/nile-file-upload/utils/file-validation.util.ts +10 -5
  267. package/src/nile-filter-chip/nile-filter-chip.css.ts +22 -0
  268. package/src/nile-filter-chip/nile-filter-chip.ts +2 -0
  269. package/src/nile-inline-edit/nile-inline-edit.css.ts +15 -6
  270. package/src/nile-inline-edit/nile-inline-edit.ts +4 -0
  271. package/src/nile-inline-sidebar/nile-inline-sidebar.css.ts +19 -0
  272. package/src/nile-inline-sidebar/nile-inline-sidebar.test.ts +108 -0
  273. package/src/nile-inline-sidebar/nile-inline-sidebar.ts +108 -5
  274. package/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.ts +107 -0
  275. package/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.ts +107 -0
  276. package/src/nile-inline-sidebar-item/nile-inline-sidebar-item.ts +34 -3
  277. package/src/nile-input/nile-input.css.ts +4 -4
  278. package/src/nile-link/nile-link.css.ts +1 -2
  279. package/src/nile-link/nile-link.ts +1 -0
  280. package/src/nile-pagination/nile-pagination.css.ts +107 -3
  281. package/src/nile-pagination/nile-pagination.test.ts +1388 -101
  282. package/src/nile-pagination/nile-pagination.ts +87 -2
  283. package/src/nile-qr-code/index.ts +1 -0
  284. package/src/nile-qr-code/nile-qr-code-utils.ts +733 -0
  285. package/src/nile-qr-code/nile-qr-code.css.ts +26 -0
  286. package/src/nile-qr-code/nile-qr-code.test.ts +879 -0
  287. package/src/nile-qr-code/nile-qr-code.ts +431 -0
  288. package/src/nile-radio/nile-radio.css.ts +1 -1
  289. package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +25 -6
  290. package/src/nile-rich-text-editor/nile-rich-text-editor.ts +14 -6
  291. package/src/nile-rich-text-editor/nile-rte-select.ts +5 -2
  292. package/src/nile-select/nile-select.css.ts +7 -4
  293. package/src/nile-slide-toggle/nile-slide-toggle.css.ts +5 -1
  294. package/src/nile-textarea/nile-textarea.css.ts +10 -5
  295. package/src/nile-virtual-select/nile-virtual-select.css.ts +5 -3
  296. package/vscode-html-custom-data.json +163 -10
  297. package/web-test-runner.config.mjs +1 -1
@@ -585,7 +585,13 @@ export class NileCodeEditor extends NileElement {
585
585
  }
586
586
 
587
587
  getReadOnlyExtension() {
588
- return (this.readonly || this.disabled) ? EditorState.readOnly.of(true) : [];
588
+ if(this.readonly || this.disabled) {
589
+ return [
590
+ EditorState.readOnly.of(true),
591
+ EditorView.editable.of(false),
592
+ ]
593
+ }
594
+ return [];
589
595
  }
590
596
 
591
597
  getSingleLineExtension() {
@@ -81,6 +81,8 @@ export class NileDatePicker extends NileElement {
81
81
 
82
82
  @property({ type: Boolean, attribute: true, reflect: true }) portal = false;
83
83
 
84
+ @property({ type: Boolean, attribute: true, reflect: true }) disabled = false;
85
+
84
86
  @query('nile-dropdown') dropdown: NileDropdown;
85
87
 
86
88
  /**
@@ -116,6 +118,23 @@ export class NileDatePicker extends NileElement {
116
118
  }
117
119
  });
118
120
  }
121
+
122
+ updated(changedProperties: Map<string, unknown>) {
123
+ super.updated(changedProperties);
124
+ if (changedProperties.has('disabled')) {
125
+ this.updateTriggerDisabledState();
126
+ }
127
+ }
128
+
129
+
130
+ private updateTriggerDisabledState() {
131
+ const trigger = this.querySelector('[slot="trigger"]') as HTMLElement | null;
132
+ if (!trigger) return;
133
+ if ('disabled' in trigger) {
134
+ (trigger as any).disabled = this.disabled;
135
+ }
136
+ }
137
+
119
138
 
120
139
  private jumpTo(date: Date) {
121
140
  this.open = true;
@@ -141,8 +160,8 @@ export class NileDatePicker extends NileElement {
141
160
 
142
161
  render(): TemplateResult {
143
162
  return html`
144
- <nile-dropdown .open="${this.open}" part="dd-base" .hoist="${true}" distance="6" exportparts="base" .portal="${this.portal}">
145
- <slot slot="trigger" part="trigger" name="trigger"></slot>
163
+ <nile-dropdown ?disabled="${this.disabled}" .open="${this.open}" part="dd-base" .hoist="${true}" distance="6" exportparts="base" .portal="${this.portal}">
164
+ <slot slot="trigger" part="trigger" name="trigger"></slot>
146
165
 
147
166
  <nile-calendar
148
167
  .hideTypes="${this.hideTypes}"
@@ -179,6 +198,7 @@ export class NileDatePicker extends NileElement {
179
198
 
180
199
  const detail = event.detail;
181
200
  const triggerInput = this.querySelector("nile-input");
201
+ if (this.disabled) return;
182
202
 
183
203
  if (!this.range) {
184
204
  const picked = detail.value;
@@ -0,0 +1 @@
1
+ export { NileDetail } from './nile-detail';
@@ -0,0 +1,153 @@
1
+ import { css } from 'lit';
2
+
3
+ export const styles = css`
4
+ :host {
5
+ box-sizing: border-box;
6
+ -webkit-font-smoothing: var(--nile-webkit-font-smoothing, var(--ng-webkit-font-smoothing));
7
+ -moz-osx-font-smoothing: var(--nile-moz-osx-font-smoothing, var(--ng-moz-osx-font-smoothing));
8
+ text-rendering: var(--nile-text-rendering, var(--ng-text-rendering));
9
+ }
10
+
11
+ :host *,
12
+ :host *::before,
13
+ :host *::after {
14
+ box-sizing: inherit;
15
+ }
16
+
17
+ [hidden] {
18
+ display: none;
19
+ }
20
+
21
+ :host {
22
+ display: block;
23
+ width: 100%;
24
+ }
25
+
26
+ .detail {
27
+ border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
28
+ border-radius: var(--nile-radius-md, var(--ng-radius-xl));
29
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary));
30
+ box-shadow: var(--nile-box-shadow-3, var(--ng-shadow-xs));
31
+ overflow: hidden;
32
+ width: 100%;
33
+ line-height: var(--nile-line-height-inherit, var(--ng-line-height-text-sm));
34
+ font-family: var(--nile-font-family-sans-serif, var(--ng-font-family-body));
35
+ }
36
+
37
+ .detail--disabled {
38
+ opacity: var(--nile-opacity-50, var(--ng-opacity-50));
39
+ }
40
+
41
+ .detail__header {
42
+ display: flex;
43
+ gap: var(--nile-spacing-lg, var(--ng-spacing-lg));
44
+ align-items: center;
45
+ border-radius: inherit;
46
+ padding: var(--nile-spacing-xl, var(--ng-spacing-xl));
47
+ background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-tertiary));
48
+ font-size: var(--nile-type-scale-4, var(--ng-spacing-xl));
49
+ font-weight: var(--nile-font-weight-semibold, var(--ng-font-weight-semibold));
50
+ color: var(--nile-colors-neutral-900, var(--ng-colors-text-primary));
51
+ user-select: none;
52
+ cursor: pointer;
53
+ list-style: none;
54
+ }
55
+
56
+ .detail__header::-webkit-details-marker {
57
+ display: none;
58
+ }
59
+
60
+ .detail__header::marker {
61
+ content: '';
62
+ }
63
+
64
+ .detail__header--icon-left {
65
+ flex-direction: row-reverse;
66
+ }
67
+
68
+ .detail__header:focus {
69
+ outline: none;
70
+ }
71
+
72
+ .detail__header:focus-visible {
73
+ outline: solid 3px var(--nile-colors-blue-500, var(--ng-colors-fg-brand-primary-600));
74
+ outline-offset: calc(1px + 1px);
75
+ }
76
+
77
+ .detail--disabled .detail__header {
78
+ cursor: not-allowed;
79
+ }
80
+
81
+ .detail--disabled .detail__header:focus-visible {
82
+ outline: none;
83
+ box-shadow: none;
84
+ }
85
+
86
+ .detail__label {
87
+ width: 100%;
88
+ display: flex;
89
+ flex-direction: column;
90
+ }
91
+
92
+ .detail__header-actions {
93
+ flex: 0 0 auto;
94
+ display: flex;
95
+ align-items: center;
96
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
97
+ }
98
+
99
+ .detail__prefix {
100
+ flex: 0 0 auto;
101
+ display: flex;
102
+ align-items: center;
103
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
104
+ }
105
+
106
+ .detail__suffix {
107
+ flex: 0 0 auto;
108
+ display: flex;
109
+ align-items: center;
110
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
111
+ }
112
+
113
+ .detail__heading-text {
114
+ font-weight: var(--nile-font-weight-semibold, var(--ng-font-weight-semibold));
115
+ font-family: var(--nile-font-family-medium, var(--ng-font-family-body));
116
+ font-size: var(--nile-type-scale-4, var(--ng-spacing-xl));
117
+ }
118
+
119
+ .detail__description {
120
+ font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
121
+ font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));
122
+ color: var(--nile-colors-dark-500, var(--ng-colors-text-secondary));
123
+ margin-top: 2px;
124
+ }
125
+
126
+ .detail__summary-icon {
127
+ flex: 0 0 auto;
128
+ display: flex;
129
+ align-items: center;
130
+ transition: 250ms transform ease;
131
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-secondary));
132
+ transform: rotate(90deg);
133
+ }
134
+
135
+ .detail--open .detail__summary-icon {
136
+ transform: rotate(-90deg);
137
+ }
138
+
139
+ .detail__body {
140
+ overflow: hidden;
141
+ }
142
+
143
+ .detail__body[hidden] {
144
+ display: none;
145
+ }
146
+
147
+ .detail__content {
148
+ display: block;
149
+ padding: var(--nile-spacing-xl, var(--ng-spacing-xl));
150
+ }
151
+ `;
152
+
153
+ export default [styles];
@@ -0,0 +1,215 @@
1
+ import { expect, fixture, html, oneEvent, aTimeout } from '@open-wc/testing';
2
+ import './nile-detail';
3
+ import type { NileDetail } from './nile-detail';
4
+
5
+ describe('NileDetail', () => {
6
+
7
+ /* Rendering & Structure */
8
+
9
+ it('1. renders without errors', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el).to.exist; });
10
+ it('2. has shadow root', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot).to.not.be.null; });
11
+ it('3. is registered as custom element', async () => { expect(customElements.get('nile-detail')).to.exist; });
12
+ it('4. tag name is nile-detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.tagName.toLowerCase()).to.equal('nile-detail'); });
13
+ it('5. uses native details element', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('details')).to.exist; });
14
+ it('6. uses native summary element', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')).to.exist; });
15
+ it('7. has static styles', async () => { expect((await import('./nile-detail')).NileDetail.styles).to.exist; });
16
+ it('8. shadow mode is open', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.mode).to.equal('open'); });
17
+
18
+ /* Default property values */
19
+
20
+ it('9. open defaults to false', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.open).to.be.false; });
21
+ it('10. heading defaults to empty string', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.heading).to.equal(''); });
22
+ it('11. description defaults to empty string', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.description).to.equal(''); });
23
+ it('12. expandIconPlacement defaults to right', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.expandIconPlacement).to.equal('right'); });
24
+ it('13. disabled defaults to false', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.disabled).to.be.false; });
25
+
26
+ /* Attribute reflection */
27
+
28
+ it('14. open attribute reflects to property', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); expect(el.open).to.be.true; });
29
+ it('15. heading attribute reflects to property', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Applications"></nile-detail>`); expect(el.heading).to.equal('Applications'); });
30
+ it('16. description attribute reflects to property', async () => { const el = await fixture<NileDetail>(html`<nile-detail description="Manage your apps"></nile-detail>`); expect(el.description).to.equal('Manage your apps'); });
31
+ it('17. disabled attribute reflects to property', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled></nile-detail>`); expect(el.disabled).to.be.true; });
32
+ it('18. expandIconPlacement attribute reflects to property', async () => { const el = await fixture<NileDetail>(html`<nile-detail expandIconPlacement="left"></nile-detail>`); expect(el.expandIconPlacement).to.equal('left'); });
33
+ it('19. setting open property reflects to attribute', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); el.open = true; await el.updateComplete; expect(el.hasAttribute('open')).to.be.true; });
34
+ it('20. removing open attribute sets property to false', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); el.removeAttribute('open'); await el.updateComplete; expect(el.open).to.be.false; });
35
+
36
+ /* Heading and description rendering */
37
+
38
+ it('21. renders heading text in the header', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Settings"></nile-detail>`); const text = el.shadowRoot!.querySelector('.detail__heading-text'); expect(text!.textContent).to.equal('Settings'); });
39
+ it('22. renders description when provided', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Title" description="Subtitle here"></nile-detail>`); const desc = el.shadowRoot!.querySelector('.detail__description'); expect(desc).to.exist; expect(desc!.textContent).to.equal('Subtitle here'); });
40
+ it('23. does not render description when empty', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Title"></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__description')).to.be.null; });
41
+ it('24. updates heading dynamically', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Old"></nile-detail>`); el.heading = 'New'; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail__heading-text')!.textContent).to.equal('New'); });
42
+ it('25. updates description dynamically', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" description="Old"></nile-detail>`); el.description = 'New'; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail__description')!.textContent).to.equal('New'); });
43
+
44
+ /* CSS classes driven by state */
45
+
46
+ it('26. adds detail--open class when open', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail--open')).to.exist; });
47
+ it('27. no detail--open class when closed', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail--open')).to.be.null; });
48
+ it('28. adds detail--disabled class when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail--disabled')).to.exist; });
49
+ it('29. no detail--disabled class when enabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail--disabled')).to.be.null; });
50
+ it('30. adds detail__header--icon-left when expandIconPlacement is left', async () => { const el = await fixture<NileDetail>(html`<nile-detail expandIconPlacement="left"></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.exist; });
51
+ it('31. no detail__header--icon-left when expandIconPlacement is right', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.be.null; });
52
+
53
+ /* Accessibility — ARIA & keyboard */
54
+
55
+ it('32. summary has aria-label matching heading', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps"></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('aria-label')).to.equal('Apps'); });
56
+ it('33. summary has aria-label matching heading and description', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps" description="Manage apps"></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('aria-label')).to.equal('Apps, Manage apps'); });
57
+ it('34. summary has aria-disabled false when enabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('aria-disabled')).to.equal('false'); });
58
+ it('35. summary has aria-disabled true when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('aria-disabled')).to.equal('true'); });
59
+ it('36. summary has tabindex 0 when enabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('tabindex')).to.equal('0'); });
60
+ it('37. summary has tabindex -1 when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('tabindex')).to.equal('-1'); });
61
+ it('38. label slot has aria-hidden true', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps"></nile-detail>`); expect(el.shadowRoot!.querySelector('slot[name="label"]')!.getAttribute('aria-hidden')).to.equal('true'); });
62
+ it('39. summary has aria-expanded false when closed', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('summary')!.getAttribute('aria-expanded')).to.equal('false'); });
63
+ it('40. summary-icon container has aria-hidden true', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__summary-icon')!.getAttribute('aria-hidden')).to.equal('true'); });
64
+
65
+ /* Parts */
66
+
67
+ it('41. exposes base part', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('[part="base"]')!.tagName.toLowerCase()).to.equal('details'); });
68
+ it('42. exposes header part', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('[part="header"]')!.tagName.toLowerCase()).to.equal('summary'); });
69
+ it('43. exposes summary-icon part', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('[part="summary-icon"]')).to.exist; });
70
+ it('44. exposes content part on body', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('[part="content"]')).to.exist; });
71
+
72
+ /* Slots */
73
+
74
+ it('45. has header-description slot', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('slot[name="header-description"]')).to.exist; });
75
+ it('46. has icon slot', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('slot[name="icon"]')).to.exist; });
76
+ it('47. has default slot for content', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('slot:not([name])')).to.exist; });
77
+ it('48. user can slot custom heading markup', async () => { const el = await fixture<NileDetail>(html`<nile-detail><div slot="header-description"><strong>Custom</strong></div></nile-detail>`); expect(el.querySelector('[slot="header-description"]')).to.exist; });
78
+ it('49. user can slot custom icon', async () => { const el = await fixture<NileDetail>(html`<nile-detail><span slot="icon">▶</span></nile-detail>`); expect(el.querySelector('[slot="icon"]')).to.exist; });
79
+ it('50. default slot receives content', async () => { const el = await fixture<NileDetail>(html`<nile-detail><p>Some content</p></nile-detail>`); expect(el.querySelector('p')!.textContent).to.equal('Some content'); });
80
+
81
+ /* Body visibility after first render */
82
+
83
+ it('51. body is hidden when initially closed', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.body.hidden).to.be.true; });
84
+ it('52. body is visible when initially open', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); expect(el.body.hidden).to.be.false; });
85
+ it('53. body height is 0 when initially closed', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.body.style.height).to.equal('0'); });
86
+ it('54. body height is auto when initially open', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); expect(el.body.style.height).to.equal('auto'); });
87
+
88
+ /* show() and hide() methods */
89
+
90
+ it('55. show method exists', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.show).to.be.a('function'); });
91
+ it('56. hide method exists', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.hide).to.be.a('function'); });
92
+ it('57. show() opens a closed detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps"><p>Content</p></nile-detail>`); el.show(); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
93
+ it('58. hide() closes an open detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps" open><p>Content</p></nile-detail>`); el.hide(); const ev = await oneEvent(el, 'nile-after-hide'); expect(ev).to.exist; expect(el.open).to.be.false; });
94
+ it('59. show() is no-op when already open', async () => { const el = await fixture<NileDetail>(html`<nile-detail open><p>C</p></nile-detail>`); const result = await el.show(); expect(result).to.be.undefined; expect(el.open).to.be.true; });
95
+ it('60. hide() is no-op when already closed', async () => { const el = await fixture<NileDetail>(html`<nile-detail><p>C</p></nile-detail>`); const result = await el.hide(); expect(result).to.be.undefined; expect(el.open).to.be.false; });
96
+ it('61. show() is no-op when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled><p>C</p></nile-detail>`); const result = await el.show(); expect(result).to.be.undefined; expect(el.open).to.be.false; });
97
+ it('62. hide() is no-op when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail open disabled><p>C</p></nile-detail>`); const result = await el.hide(); expect(result).to.be.undefined; expect(el.open).to.be.true; });
98
+
99
+ /* Events — nile-show, nile-after-show, nile-hide, nile-after-hide */
100
+
101
+ it('63. emits nile-show when opening', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); setTimeout(() => el.show()); const ev = await oneEvent(el, 'nile-show'); expect(ev).to.exist; });
102
+ it('64. emits nile-after-show after open animation', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); setTimeout(() => el.show()); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; });
103
+ it('65. emits nile-hide when closing', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); setTimeout(() => el.hide()); const ev = await oneEvent(el, 'nile-hide'); expect(ev).to.exist; });
104
+ it('66. emits nile-after-hide after close animation', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); setTimeout(() => el.hide()); const ev = await oneEvent(el, 'nile-after-hide'); expect(ev).to.exist; });
105
+
106
+ /* Cancelable events */
107
+
108
+ it('67. canceling nile-show prevents opening', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); el.addEventListener('nile-show', (e: Event) => e.preventDefault()); el.open = true; await aTimeout(400); expect(el.open).to.be.false; });
109
+ it('68. canceling nile-hide prevents closing', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); el.addEventListener('nile-hide', (e: Event) => e.preventDefault()); el.open = false; await aTimeout(400); expect(el.open).to.be.true; });
110
+
111
+ /* Disabled prevents interaction */
112
+
113
+ it('69. clicking summary does not open when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" disabled><p>C</p></nile-detail>`); el.shadowRoot!.querySelector('summary')!.click(); await el.updateComplete; expect(el.open).to.be.false; });
114
+ it('70. clicking summary does not close when disabled', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open disabled><p>C</p></nile-detail>`); el.shadowRoot!.querySelector('summary')!.click(); await el.updateComplete; expect(el.open).to.be.true; });
115
+ it('71. enabling a disabled detail allows interaction again', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" disabled><p>C</p></nile-detail>`); el.disabled = false; await el.updateComplete; el.show(); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
116
+
117
+ /* Dynamic state changes */
118
+
119
+ it('72. toggling open on then off removes detail--open class', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); el.open = true; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail--open')).to.exist; el.open = false; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail--open')).to.be.null; });
120
+ it('73. toggling disabled on then off removes detail--disabled class', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); el.disabled = true; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail--disabled')).to.exist; el.disabled = false; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail--disabled')).to.be.null; });
121
+ it('74. switching expandIconPlacement from right to left updates class', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.be.null; el.expandIconPlacement = 'left'; await el.updateComplete; expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.exist; });
122
+ it('75. native details open attribute updates when open changes', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('details')!.open).to.be.false; el.open = true; await el.updateComplete; });
123
+
124
+ /* Keyboard interaction */
125
+
126
+ it('76. Enter key on summary opens the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
127
+ it('77. Space key on summary opens the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
128
+ it('78. Enter key on summary closes an open detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-hide'); expect(ev).to.exist; expect(el.open).to.be.false; });
129
+ it('79. ArrowDown key opens the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
130
+ it('80. ArrowUp key closes the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-hide'); expect(ev).to.exist; expect(el.open).to.be.false; });
131
+ it('81. ArrowRight key opens the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-show'); expect(ev).to.exist; expect(el.open).to.be.true; });
132
+ it('82. ArrowLeft key closes the detail', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>C</p></nile-detail>`); const summary = el.shadowRoot!.querySelector('summary')!; summary.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true, composed: true })); const ev = await oneEvent(el, 'nile-after-hide'); expect(ev).to.exist; expect(el.open).to.be.false; });
133
+
134
+ /* Multiple instances are independent */
135
+
136
+ it('83. two details operate independently', async () => { const c = await fixture(html`<div><nile-detail heading="A"><p>A</p></nile-detail><nile-detail heading="B"><p>B</p></nile-detail></div>`); const [a, b] = Array.from(c.querySelectorAll('nile-detail')) as NileDetail[]; a.show(); await oneEvent(a, 'nile-after-show'); expect(a.open).to.be.true; expect(b.open).to.be.false; });
137
+ it('84. opening one does not close the other', async () => { const c = await fixture(html`<div><nile-detail heading="A" open><p>A</p></nile-detail><nile-detail heading="B"><p>B</p></nile-detail></div>`); const [a, b] = Array.from(c.querySelectorAll('nile-detail')) as NileDetail[]; b.show(); await oneEvent(b, 'nile-after-show'); expect(a.open).to.be.true; expect(b.open).to.be.true; });
138
+
139
+ /* DOM lifecycle */
140
+
141
+ it('85. isConnected is true when in DOM', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.isConnected).to.be.true; });
142
+ it('86. isConnected is false after removal', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); el.remove(); expect(el.isConnected).to.be.false; });
143
+ it('87. can be created via document.createElement', async () => { const el = document.createElement('nile-detail') as NileDetail; document.body.appendChild(el); await el.updateComplete; expect(el.shadowRoot).to.not.be.null; document.body.removeChild(el); });
144
+
145
+ /* Generic HTML attributes pass through */
146
+
147
+ it('88. class attribute works', async () => { const el = await fixture<NileDetail>(html`<nile-detail class="custom"></nile-detail>`); expect(el.classList.contains('custom')).to.be.true; });
148
+ it('89. id attribute works', async () => { const el = await fixture<NileDetail>(html`<nile-detail id="d1"></nile-detail>`); expect(el.id).to.equal('d1'); });
149
+ it('90. hidden attribute works', async () => { const el = await fixture<NileDetail>(html`<nile-detail hidden></nile-detail>`); expect(el.hidden).to.be.true; });
150
+ it('91. data attribute works', async () => { const el = await fixture<NileDetail>(html`<nile-detail data-section="apps"></nile-detail>`); expect(el.dataset.section).to.equal('apps'); });
151
+ it('92. style attribute works', async () => { const el = await fixture<NileDetail>(html`<nile-detail style="margin-top:10px"></nile-detail>`); expect(el.style.marginTop).to.equal('10px'); });
152
+ it('93. aria-label passes through', async () => { const el = await fixture<NileDetail>(html`<nile-detail aria-label="Section"></nile-detail>`); expect(el.getAttribute('aria-label')).to.equal('Section'); });
153
+
154
+ /* Icon element */
155
+
156
+ it('94. renders nile-icon by default', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('nile-icon')).to.exist; });
157
+ it('95. icon container exists', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el.shadowRoot!.querySelector('.detail__summary-icon')).to.exist; });
158
+
159
+ /* Open/close sequence */
160
+
161
+ it('96. open then close returns to original state', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); el.show(); await oneEvent(el, 'nile-after-show'); expect(el.open).to.be.true; el.hide(); await oneEvent(el, 'nile-after-hide'); expect(el.open).to.be.false; expect(el.body.hidden).to.be.true; });
162
+ it('97. rapid show then hide settles correctly', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T"><p>C</p></nile-detail>`); el.show(); await aTimeout(50); el.hide(); await oneEvent(el, 'nile-after-hide'); expect(el.open).to.be.false; });
163
+
164
+ /* Full integration */
165
+
166
+ it('98. full integration with all properties set', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Apps" description="Manage" expandIconPlacement="left" class="panel" id="p1"><p>Content</p></nile-detail>`); expect(el.heading).to.equal('Apps'); expect(el.description).to.equal('Manage'); expect(el.expandIconPlacement).to.equal('left'); expect(el.id).to.equal('p1'); expect(el.open).to.be.false; expect(el.disabled).to.be.false; expect(el.shadowRoot!.querySelector('details')).to.exist; expect(el.shadowRoot!.querySelector('summary')).to.exist; expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.exist; expect(el.querySelector('p')!.textContent).to.equal('Content'); });
167
+ it('99. combined disabled and open set at init', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open disabled><p>C</p></nile-detail>`); expect(el.open).to.be.true; expect(el.disabled).to.be.true; expect(el.shadowRoot!.querySelector('.detail--open')).to.exist; expect(el.shadowRoot!.querySelector('.detail--disabled')).to.exist; expect(el.shadowRoot!.querySelector('summary')!.getAttribute('tabindex')).to.equal('-1'); });
168
+ it('100. dispatchEvent works for custom events', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); let detail: any; el.addEventListener('custom-event', ((e: CustomEvent) => { detail = e.detail; }) as EventListener); el.dispatchEvent(new CustomEvent('custom-event', { detail: { test: true } })); expect(detail.test).to.be.true; });
169
+
170
+ /* Partial / missing properties — user does not pass everything */
171
+
172
+ it('101. works with no properties at all', async () => { const el = await fixture<NileDetail>(html`<nile-detail></nile-detail>`); expect(el).to.exist; expect(el.open).to.be.false; expect(el.heading).to.equal(''); expect(el.description).to.equal(''); expect(el.disabled).to.be.false; expect(el.shadowRoot!.querySelector('details')).to.exist; });
173
+ it('102. works with only heading, no description', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Section"></nile-detail>`); expect(el.heading).to.equal('Section'); expect(el.shadowRoot!.querySelector('.detail__description')).to.be.null; expect(el.shadowRoot!.querySelector('.detail__heading-text')!.textContent).to.equal('Section'); });
174
+ it('103. works with only description, no heading', async () => { const el = await fixture<NileDetail>(html`<nile-detail description="Some info"></nile-detail>`); expect(el.heading).to.equal(''); expect(el.description).to.equal('Some info'); expect(el.shadowRoot!.querySelector('.detail__heading-text')!.textContent).to.equal(''); expect(el.shadowRoot!.querySelector('.detail__description')!.textContent).to.equal('Some info'); });
175
+ it('104. works with only open, no heading or content', async () => { const el = await fixture<NileDetail>(html`<nile-detail open></nile-detail>`); expect(el.open).to.be.true; expect(el.heading).to.equal(''); expect(el.body.hidden).to.be.false; });
176
+ it('105. works with only disabled, no heading or content', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled></nile-detail>`); expect(el.disabled).to.be.true; expect(el.shadowRoot!.querySelector('.detail--disabled')).to.exist; });
177
+ it('106. works with content but no heading or description', async () => { const el = await fixture<NileDetail>(html`<nile-detail><p>Just content</p></nile-detail>`); expect(el.heading).to.equal(''); expect(el.querySelector('p')!.textContent).to.equal('Just content'); });
178
+ it('107. works with only expandIconPlacement set', async () => { const el = await fixture<NileDetail>(html`<nile-detail expandIconPlacement="left"></nile-detail>`); expect(el.expandIconPlacement).to.equal('left'); expect(el.shadowRoot!.querySelector('.detail__header--icon-left')).to.exist; });
179
+ it('108. works with heading and open, no content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Empty" open></nile-detail>`); expect(el.open).to.be.true; expect(el.heading).to.equal('Empty'); expect(el.children.length).to.equal(0); });
180
+ it('109. works with empty string heading attribute', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading=""></nile-detail>`); expect(el.heading).to.equal(''); expect(el.shadowRoot!.querySelector('.detail__heading-text')!.textContent).to.equal(''); });
181
+ it('110. works with only slotted heading, no heading property', async () => { const el = await fixture<NileDetail>(html`<nile-detail><div slot="header-description"><b>Custom Header</b></div></nile-detail>`); expect(el.heading).to.equal(''); expect(el.querySelector('[slot="header-description"]')).to.exist; });
182
+ it('111. works with only slotted icon, no other props', async () => { const el = await fixture<NileDetail>(html`<nile-detail><span slot="icon">→</span></nile-detail>`); expect(el.querySelector('[slot="icon"]')!.textContent).to.equal('→'); });
183
+ it('112. works with disabled and content but no heading', async () => { const el = await fixture<NileDetail>(html`<nile-detail disabled><p>Locked</p></nile-detail>`); expect(el.disabled).to.be.true; expect(el.querySelector('p')!.textContent).to.equal('Locked'); const result = await el.show(); expect(result).to.be.undefined; expect(el.open).to.be.false; });
184
+ it('113. works with open and disabled but no content', async () => { const el = await fixture<NileDetail>(html`<nile-detail open disabled></nile-detail>`); expect(el.open).to.be.true; expect(el.disabled).to.be.true; expect(el.children.length).to.equal(0); });
185
+
186
+ /* User can pass anything inside the content */
187
+
188
+ it('114. accepts plain text as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open>Hello World</nile-detail>`); expect(el.textContent).to.contain('Hello World'); });
189
+ it('115. accepts a paragraph as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>Paragraph</p></nile-detail>`); expect(el.querySelector('p')!.textContent).to.equal('Paragraph'); });
190
+ it('116. accepts multiple paragraphs as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="T" open><p>One</p><p>Two</p><p>Three</p></nile-detail>`); expect(el.querySelectorAll('p').length).to.equal(3); });
191
+ it('117. accepts a table as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Data" open><table><tr><td>Cell</td></tr></table></nile-detail>`); expect(el.querySelector('table')).to.exist; expect(el.querySelector('td')!.textContent).to.equal('Cell'); });
192
+ it('118. accepts a form as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Settings" open><form><input type="text" value="test"><button type="submit">Save</button></form></nile-detail>`); expect(el.querySelector('form')).to.exist; expect(el.querySelector('input')).to.exist; expect(el.querySelector('button')!.textContent).to.equal('Save'); });
193
+ it('119. accepts an image as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Photo" open><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="pixel"></nile-detail>`); expect(el.querySelector('img')).to.exist; expect(el.querySelector('img')!.alt).to.equal('pixel'); });
194
+ it('120. accepts an unordered list as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Items" open><ul><li>A</li><li>B</li><li>C</li></ul></nile-detail>`); expect(el.querySelectorAll('li').length).to.equal(3); });
195
+ it('121. accepts an ordered list as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Steps" open><ol><li>First</li><li>Second</li></ol></nile-detail>`); expect(el.querySelector('ol')).to.exist; expect(el.querySelectorAll('li').length).to.equal(2); });
196
+ it('122. accepts a video element as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Media" open><video src="test.mp4"></video></nile-detail>`); expect(el.querySelector('video')).to.exist; });
197
+ it('123. accepts an iframe as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Embed" open><iframe title="embed"></iframe></nile-detail>`); expect(el.querySelector('iframe')).to.exist; });
198
+ it('124. accepts deeply nested HTML as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Nested" open><div><section><article><p>Deep</p></article></section></div></nile-detail>`); expect(el.querySelector('article p')!.textContent).to.equal('Deep'); });
199
+ it('125. accepts a canvas element as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Draw" open><canvas width="100" height="100"></canvas></nile-detail>`); expect(el.querySelector('canvas')).to.exist; });
200
+ it('126. accepts links as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Links" open><a href="#">Link 1</a><a href="#">Link 2</a></nile-detail>`); expect(el.querySelectorAll('a').length).to.equal(2); });
201
+ it('127. accepts code blocks as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Code" open><pre><code>const x = 1;</code></pre></nile-detail>`); expect(el.querySelector('code')!.textContent).to.equal('const x = 1;'); });
202
+ it('128. accepts styled divs as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Styled" open><div style="background:red;padding:10px">Red box</div></nile-detail>`); const div = el.querySelector('div')!; expect(div.style.background).to.equal('red'); expect(div.textContent).to.equal('Red box'); });
203
+ it('129. accepts SVG as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Graphic" open><svg width="10" height="10"><circle cx="5" cy="5" r="5"></circle></svg></nile-detail>`); expect(el.querySelector('svg')).to.exist; expect(el.querySelector('circle')).to.exist; });
204
+ it('130. accepts another web component as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Nested Component" open><nile-detail heading="Inner"><p>Inner content</p></nile-detail></nile-detail>`); const inner = el.querySelector('nile-detail') as NileDetail; expect(inner).to.exist; expect(inner.heading).to.equal('Inner'); });
205
+ it('131. accepts mixed content types together', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Mixed" open><p>Text</p><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="img"><ul><li>Item</li></ul><table><tr><td>Cell</td></tr></table></nile-detail>`); expect(el.querySelector('p')).to.exist; expect(el.querySelector('img')).to.exist; expect(el.querySelector('ul')).to.exist; expect(el.querySelector('table')).to.exist; });
206
+ it('132. accepts empty content gracefully', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Empty" open></nile-detail>`); expect(el).to.exist; expect(el.children.length).to.equal(0); });
207
+ it('133. accepts content with data attributes', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Data" open><div data-id="123" data-type="card">Item</div></nile-detail>`); const div = el.querySelector('div')!; expect(div.dataset.id).to.equal('123'); expect(div.dataset.type).to.equal('card'); });
208
+ it('134. accepts input elements and they remain functional', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Form" open><input type="text" id="inp"></nile-detail>`); const input = el.querySelector('input')! as HTMLInputElement; input.value = 'typed'; expect(input.value).to.equal('typed'); });
209
+ it('135. accepts textarea as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Notes" open><textarea>Some notes</textarea></nile-detail>`); expect(el.querySelector('textarea')!.value).to.equal('Some notes'); });
210
+ it('136. accepts select dropdown as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Options" open><select><option value="a">A</option><option value="b">B</option></select></nile-detail>`); expect(el.querySelectorAll('option').length).to.equal(2); });
211
+ it('137. accepts checkbox inputs as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Prefs" open><label><input type="checkbox" checked> Agree</label></nile-detail>`); const cb = el.querySelector('input[type="checkbox"]') as HTMLInputElement; expect(cb.checked).to.be.true; });
212
+ it('138. accepts radio buttons as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Choice" open><input type="radio" name="r" value="1"><input type="radio" name="r" value="2"></nile-detail>`); expect(el.querySelectorAll('input[type="radio"]').length).to.equal(2); });
213
+ it('139. accepts definition list as content', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Glossary" open><dl><dt>Term</dt><dd>Definition</dd></dl></nile-detail>`); expect(el.querySelector('dt')!.textContent).to.equal('Term'); expect(el.querySelector('dd')!.textContent).to.equal('Definition'); });
214
+ it('140. content with event listeners still works', async () => { const el = await fixture<NileDetail>(html`<nile-detail heading="Interactive" open><button id="btn">Click</button></nile-detail>`); let clicked = false; el.querySelector('#btn')!.addEventListener('click', () => { clicked = true; }); (el.querySelector('#btn') as HTMLElement).click(); expect(clicked).to.be.true; });
215
+ });
@@ -0,0 +1,140 @@
1
+ import { html, TemplateResult, nothing } from 'lit';
2
+ import { customElement, property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { styles } from './nile-detail.css';
5
+ import NileElement from '../internal/nile-element';
6
+ import { watch } from '../internal/watch';
7
+ import {
8
+ animateShow,
9
+ animateHide,
10
+ showDetail,
11
+ hideDetail,
12
+ handleSummaryClick,
13
+ handleSummaryKeyDown,
14
+ } from './nile-detail.utils';
15
+ import type { CSSResultGroup } from 'lit';
16
+
17
+ @customElement('nile-detail')
18
+ export class NileDetail extends NileElement {
19
+
20
+ static styles: CSSResultGroup = styles;
21
+
22
+ @query('details') detail: HTMLDetailsElement;
23
+ @query('summary') header: HTMLElement;
24
+ @query('.detail__body') body: HTMLElement;
25
+
26
+ @property({ attribute: true, type: Boolean, reflect: true }) open = false;
27
+
28
+ @property({ attribute: true, type: String, reflect: true }) heading = '';
29
+
30
+ @property({ attribute: true, type: String, reflect: true }) description = '';
31
+
32
+ @property({ attribute: true, reflect: true }) expandIconPlacement: 'left' | 'right' = 'right';
33
+
34
+ @property({ attribute: true, type: Boolean, reflect: true }) disabled = false;
35
+
36
+ @state() private _detailOpen = false;
37
+
38
+ firstUpdated() {
39
+ this._detailOpen = this.open;
40
+ this.body.hidden = !this.open;
41
+ this.body.style.height = this.open ? 'auto' : '0';
42
+ }
43
+
44
+ private _handleSummaryClick(event: Event) {
45
+ event.preventDefault();
46
+ handleSummaryClick(this);
47
+ }
48
+
49
+ private _handleSummaryKeyDown(event: KeyboardEvent) {
50
+ handleSummaryKeyDown(event, this);
51
+ }
52
+
53
+ @watch('open', { waitUntilFirstUpdate: true })
54
+ async handleOpenChange() {
55
+ if (this.open) {
56
+ this._detailOpen = true;
57
+ await animateShow(this);
58
+ } else {
59
+ await animateHide(this);
60
+ this._detailOpen = false;
61
+ }
62
+ }
63
+
64
+ async show() {
65
+ return showDetail(this);
66
+ }
67
+
68
+ async hide() {
69
+ return hideDetail(this);
70
+ }
71
+
72
+ private get _summaryLabel(): string {
73
+ return [this.heading, this.description].filter(Boolean).join(', ');
74
+ }
75
+
76
+ render(): TemplateResult {
77
+ return html`
78
+ <details
79
+ part="base"
80
+ ?open=${this._detailOpen}
81
+ class=${classMap({
82
+ 'detail': true,
83
+ 'detail--open': this.open,
84
+ 'detail--disabled': this.disabled,
85
+ })}
86
+ >
87
+ <summary
88
+ part="header"
89
+ id="header"
90
+ class=${classMap({
91
+ 'detail__header': true,
92
+ 'detail__header--icon-left': this.expandIconPlacement === 'left',
93
+ })}
94
+ aria-label=${this._summaryLabel || nothing}
95
+ aria-expanded=${this.open ? 'true' : 'false'}
96
+ aria-disabled=${this.disabled ? 'true' : 'false'}
97
+ tabindex=${this.disabled ? '-1' : '0'}
98
+ @click=${this._handleSummaryClick}
99
+ @keydown=${this._handleSummaryKeyDown}
100
+ >
101
+ <slot name="label" part="label" class="detail__label">
102
+ <span part="label-text" class="detail__heading-text">${this.heading}</span>
103
+ ${this.description
104
+ ? html`<span part="description" class="detail__description">${this.description}</span>`
105
+ : ''}
106
+ </slot>
107
+
108
+ <slot name="header-actions" part="header-actions" class="detail__header-actions"></slot>
109
+
110
+ <slot name="prefix" part="prefix" class="detail__prefix"></slot>
111
+
112
+ <span part="summary-icon" class="detail__summary-icon" aria-hidden="true">
113
+ <slot name="expand-icon">
114
+ <nile-icon
115
+ name="var(--nile-icon-arrow-right, var(--ng-icon-chevron-right))"
116
+ method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
117
+ library="system"
118
+ ></nile-icon>
119
+ </slot>
120
+ </span>
121
+
122
+ <slot name="suffix" part="suffix" class="detail__suffix"></slot>
123
+
124
+ </summary>
125
+
126
+ <div part="content" class="detail__body">
127
+ <slot id="content" class="detail__content"></slot>
128
+ </div>
129
+ </details>
130
+ `;
131
+ }
132
+ }
133
+
134
+ export default NileDetail;
135
+
136
+ declare global {
137
+ interface HTMLElementTagNameMap {
138
+ 'nile-detail': NileDetail;
139
+ }
140
+ }