@cratis/scene.primereact 4.6.0 → 4.8.0

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 (388) hide show
  1. package/.storybook/preview.tsx +8 -1
  2. package/EnumerationResolution.ts +7 -0
  3. package/README.md +22 -12
  4. package/data/DataGallery.stories.tsx +6 -0
  5. package/data/DataScrollerRow.tsx +35 -0
  6. package/data/PrimeDataScroller.tsx +84 -0
  7. package/data/PrimeTreeTable.tsx +145 -0
  8. package/data/TreeTableCheckState.ts +9 -0
  9. package/data/TreeTableRow.ts +22 -0
  10. package/data/TreeTableRowView.tsx +86 -0
  11. package/data/TreeTableSelectionMode.ts +10 -0
  12. package/data/checkboxSelection.ts +92 -0
  13. package/data/index.ts +4 -0
  14. package/data/isSerializedLegacyElement.ts +30 -0
  15. package/data/readableValue.ts +32 -0
  16. package/data/selectionKeysOf.ts +40 -0
  17. package/data/treeTableRows.ts +32 -0
  18. package/data/useIncrementalLoad.ts +36 -0
  19. package/data/useLoadOnReach.ts +31 -0
  20. package/data/useTreeGridNavigation.ts +81 -0
  21. package/dist/cjs/data/DataScrollerRow.js +50 -0
  22. package/dist/cjs/data/DataScrollerRow.js.map +1 -0
  23. package/dist/cjs/data/PrimeDataScroller.js +120 -0
  24. package/dist/cjs/data/PrimeDataScroller.js.map +1 -0
  25. package/dist/cjs/data/PrimeTreeTable.js +217 -0
  26. package/dist/cjs/data/PrimeTreeTable.js.map +1 -0
  27. package/dist/cjs/data/TreeTableCheckState.js +13 -0
  28. package/dist/cjs/data/TreeTableCheckState.js.map +1 -0
  29. package/dist/cjs/data/TreeTableRowView.js +76 -0
  30. package/dist/cjs/data/TreeTableRowView.js.map +1 -0
  31. package/dist/cjs/data/TreeTableSelectionMode.js +14 -0
  32. package/dist/cjs/data/TreeTableSelectionMode.js.map +1 -0
  33. package/dist/cjs/data/checkboxSelection.js +88 -0
  34. package/dist/cjs/data/checkboxSelection.js.map +1 -0
  35. package/dist/cjs/data/isSerializedLegacyElement.js +30 -0
  36. package/dist/cjs/data/isSerializedLegacyElement.js.map +1 -0
  37. package/dist/cjs/data/readableValue.js +31 -0
  38. package/dist/cjs/data/readableValue.js.map +1 -0
  39. package/dist/cjs/data/selectionKeysOf.js +40 -0
  40. package/dist/cjs/data/selectionKeysOf.js.map +1 -0
  41. package/dist/cjs/data/treeTableRows.js +42 -0
  42. package/dist/cjs/data/treeTableRows.js.map +1 -0
  43. package/dist/cjs/data/useIncrementalLoad.js +46 -0
  44. package/dist/cjs/data/useIncrementalLoad.js.map +1 -0
  45. package/dist/cjs/data/useLoadOnReach.js +42 -0
  46. package/dist/cjs/data/useLoadOnReach.js.map +1 -0
  47. package/dist/cjs/data/useTreeGridNavigation.js +75 -0
  48. package/dist/cjs/data/useTreeGridNavigation.js.map +1 -0
  49. package/dist/cjs/editor/PrimeEditor.js +96 -0
  50. package/dist/cjs/editor/PrimeEditor.js.map +1 -0
  51. package/dist/cjs/editor/QuillLoaderContext.js +10 -0
  52. package/dist/cjs/editor/QuillLoaderContext.js.map +1 -0
  53. package/dist/cjs/editor/QuillLoaderProvider.js +21 -0
  54. package/dist/cjs/editor/QuillLoaderProvider.js.map +1 -0
  55. package/dist/cjs/editor/applyHtml.js +25 -0
  56. package/dist/cjs/editor/applyHtml.js.map +1 -0
  57. package/dist/cjs/editor/characterCount.js +10 -0
  58. package/dist/cjs/editor/characterCount.js.map +1 -0
  59. package/dist/cjs/editor/htmlToPlainText.js +101 -0
  60. package/dist/cjs/editor/htmlToPlainText.js.map +1 -0
  61. package/dist/cjs/editor/renderSafeNodes.js +35 -0
  62. package/dist/cjs/editor/renderSafeNodes.js.map +1 -0
  63. package/dist/cjs/editor/safeUrls.js +44 -0
  64. package/dist/cjs/editor/safeUrls.js.map +1 -0
  65. package/dist/cjs/editor/sanitizeHtml.js +140 -0
  66. package/dist/cjs/editor/sanitizeHtml.js.map +1 -0
  67. package/dist/cjs/editor/serializeSafeNodes.js +33 -0
  68. package/dist/cjs/editor/serializeSafeNodes.js.map +1 -0
  69. package/dist/cjs/editor/toolbarLayout.js +35 -0
  70. package/dist/cjs/editor/toolbarLayout.js.map +1 -0
  71. package/dist/cjs/editor/useQuillEditor.js +137 -0
  72. package/dist/cjs/editor/useQuillEditor.js.map +1 -0
  73. package/dist/cjs/file/FileUploadContext.js +13 -0
  74. package/dist/cjs/file/FileUploadContext.js.map +1 -0
  75. package/dist/cjs/file/FileUploadHandlerProvider.js +25 -0
  76. package/dist/cjs/file/FileUploadHandlerProvider.js.map +1 -0
  77. package/dist/cjs/file/FileUploadMode.js +13 -0
  78. package/dist/cjs/file/FileUploadMode.js.map +1 -0
  79. package/dist/cjs/file/PrimeFileUpload.js +161 -0
  80. package/dist/cjs/file/PrimeFileUpload.js.map +1 -0
  81. package/dist/cjs/file/postFiles.js +31 -0
  82. package/dist/cjs/file/postFiles.js.map +1 -0
  83. package/dist/cjs/file/resolveUploadTarget.js +68 -0
  84. package/dist/cjs/file/resolveUploadTarget.js.map +1 -0
  85. package/dist/cjs/file/useUploadSession.js +62 -0
  86. package/dist/cjs/file/useUploadSession.js.map +1 -0
  87. package/dist/cjs/index.js +32 -0
  88. package/dist/cjs/index.js.map +1 -1
  89. package/dist/cjs/legacyEnumerationChoices.js +39 -0
  90. package/dist/cjs/legacyEnumerationChoices.js.map +1 -0
  91. package/dist/cjs/loadQuill/index.js +22 -0
  92. package/dist/cjs/loadQuill/index.js.map +1 -0
  93. package/dist/cjs/primeReactComponents.js +10 -0
  94. package/dist/cjs/primeReactComponents.js.map +1 -1
  95. package/dist/cjs/primeReactDescriptors.js +243 -0
  96. package/dist/cjs/primeReactDescriptors.js.map +1 -1
  97. package/dist/cjs/primeReactPackage.js +4 -0
  98. package/dist/cjs/primeReactPackage.js.map +1 -1
  99. package/dist/cjs/resolveEnumeration.js +55 -0
  100. package/dist/cjs/resolveEnumeration.js.map +1 -0
  101. package/dist/cjs/treeNodes.js +42 -7
  102. package/dist/cjs/treeNodes.js.map +1 -1
  103. package/dist/cjs/useResettingState.js +47 -0
  104. package/dist/cjs/useResettingState.js.map +1 -0
  105. package/dist/esm/EnumerationResolution.d.ts +9 -0
  106. package/dist/esm/EnumerationResolution.d.ts.map +1 -0
  107. package/dist/esm/EnumerationResolution.js +2 -0
  108. package/dist/esm/EnumerationResolution.js.map +1 -0
  109. package/dist/esm/data/DataGallery.stories.d.ts.map +1 -1
  110. package/dist/esm/data/DataGallery.stories.js +6 -0
  111. package/dist/esm/data/DataGallery.stories.js.map +1 -1
  112. package/dist/esm/data/DataScrollerRow.d.ts +4 -0
  113. package/dist/esm/data/DataScrollerRow.d.ts.map +1 -0
  114. package/dist/esm/data/DataScrollerRow.js +48 -0
  115. package/dist/esm/data/DataScrollerRow.js.map +1 -0
  116. package/dist/esm/data/PrimeDataScroller.d.ts +3 -0
  117. package/dist/esm/data/PrimeDataScroller.d.ts.map +1 -0
  118. package/dist/esm/data/PrimeDataScroller.js +118 -0
  119. package/dist/esm/data/PrimeDataScroller.js.map +1 -0
  120. package/dist/esm/data/PrimeTreeTable.d.ts +3 -0
  121. package/dist/esm/data/PrimeTreeTable.d.ts.map +1 -0
  122. package/dist/esm/data/PrimeTreeTable.js +215 -0
  123. package/dist/esm/data/PrimeTreeTable.js.map +1 -0
  124. package/dist/esm/data/TreeTableCheckState.d.ts +6 -0
  125. package/dist/esm/data/TreeTableCheckState.d.ts.map +1 -0
  126. package/dist/esm/data/TreeTableCheckState.js +11 -0
  127. package/dist/esm/data/TreeTableCheckState.js.map +1 -0
  128. package/dist/esm/data/TreeTableRow.d.ts +10 -0
  129. package/dist/esm/data/TreeTableRow.d.ts.map +1 -0
  130. package/dist/esm/data/TreeTableRow.js +2 -0
  131. package/dist/esm/data/TreeTableRow.js.map +1 -0
  132. package/dist/esm/data/TreeTableRowView.d.ts +21 -0
  133. package/dist/esm/data/TreeTableRowView.d.ts.map +1 -0
  134. package/dist/esm/data/TreeTableRowView.js +74 -0
  135. package/dist/esm/data/TreeTableRowView.js.map +1 -0
  136. package/dist/esm/data/TreeTableSelectionMode.d.ts +7 -0
  137. package/dist/esm/data/TreeTableSelectionMode.d.ts.map +1 -0
  138. package/dist/esm/data/TreeTableSelectionMode.js +12 -0
  139. package/dist/esm/data/TreeTableSelectionMode.js.map +1 -0
  140. package/dist/esm/data/checkboxSelection.d.ts +9 -0
  141. package/dist/esm/data/checkboxSelection.d.ts.map +1 -0
  142. package/dist/esm/data/checkboxSelection.js +85 -0
  143. package/dist/esm/data/checkboxSelection.js.map +1 -0
  144. package/dist/esm/data/index.d.ts +4 -0
  145. package/dist/esm/data/index.d.ts.map +1 -1
  146. package/dist/esm/data/index.js +4 -0
  147. package/dist/esm/data/index.js.map +1 -1
  148. package/dist/esm/data/isSerializedLegacyElement.d.ts +3 -0
  149. package/dist/esm/data/isSerializedLegacyElement.d.ts.map +1 -0
  150. package/dist/esm/data/isSerializedLegacyElement.js +27 -0
  151. package/dist/esm/data/isSerializedLegacyElement.js.map +1 -0
  152. package/dist/esm/data/readableValue.d.ts +2 -0
  153. package/dist/esm/data/readableValue.d.ts.map +1 -0
  154. package/dist/esm/data/readableValue.js +29 -0
  155. package/dist/esm/data/readableValue.js.map +1 -0
  156. package/dist/esm/data/selectionKeysOf.d.ts +2 -0
  157. package/dist/esm/data/selectionKeysOf.d.ts.map +1 -0
  158. package/dist/esm/data/selectionKeysOf.js +38 -0
  159. package/dist/esm/data/selectionKeysOf.js.map +1 -0
  160. package/dist/esm/data/treeTableRows.d.ts +6 -0
  161. package/dist/esm/data/treeTableRows.d.ts.map +1 -0
  162. package/dist/esm/data/treeTableRows.js +38 -0
  163. package/dist/esm/data/treeTableRows.js.map +1 -0
  164. package/dist/esm/data/useIncrementalLoad.d.ts +6 -0
  165. package/dist/esm/data/useIncrementalLoad.d.ts.map +1 -0
  166. package/dist/esm/data/useIncrementalLoad.js +44 -0
  167. package/dist/esm/data/useIncrementalLoad.js.map +1 -0
  168. package/dist/esm/data/useLoadOnReach.d.ts +3 -0
  169. package/dist/esm/data/useLoadOnReach.d.ts.map +1 -0
  170. package/dist/esm/data/useLoadOnReach.js +40 -0
  171. package/dist/esm/data/useLoadOnReach.js.map +1 -0
  172. package/dist/esm/data/useTreeGridNavigation.d.ts +17 -0
  173. package/dist/esm/data/useTreeGridNavigation.d.ts.map +1 -0
  174. package/dist/esm/data/useTreeGridNavigation.js +73 -0
  175. package/dist/esm/data/useTreeGridNavigation.js.map +1 -0
  176. package/dist/esm/editor/PrimeEditor.d.ts +3 -0
  177. package/dist/esm/editor/PrimeEditor.d.ts.map +1 -0
  178. package/dist/esm/editor/PrimeEditor.js +94 -0
  179. package/dist/esm/editor/PrimeEditor.js.map +1 -0
  180. package/dist/esm/editor/QuillConstructor.d.ts +4 -0
  181. package/dist/esm/editor/QuillConstructor.d.ts.map +1 -0
  182. package/dist/esm/editor/QuillConstructor.js +2 -0
  183. package/dist/esm/editor/QuillConstructor.js.map +1 -0
  184. package/dist/esm/editor/QuillEditorOptions.d.ts +13 -0
  185. package/dist/esm/editor/QuillEditorOptions.d.ts.map +1 -0
  186. package/dist/esm/editor/QuillEditorOptions.js +2 -0
  187. package/dist/esm/editor/QuillEditorOptions.js.map +1 -0
  188. package/dist/esm/editor/QuillEditorState.d.ts +6 -0
  189. package/dist/esm/editor/QuillEditorState.d.ts.map +1 -0
  190. package/dist/esm/editor/QuillEditorState.js +2 -0
  191. package/dist/esm/editor/QuillEditorState.js.map +1 -0
  192. package/dist/esm/editor/QuillInstance.d.ts +16 -0
  193. package/dist/esm/editor/QuillInstance.d.ts.map +1 -0
  194. package/dist/esm/editor/QuillInstance.js +2 -0
  195. package/dist/esm/editor/QuillInstance.js.map +1 -0
  196. package/dist/esm/editor/QuillLoader.d.ts +5 -0
  197. package/dist/esm/editor/QuillLoader.d.ts.map +1 -0
  198. package/dist/esm/editor/QuillLoader.js +2 -0
  199. package/dist/esm/editor/QuillLoader.js.map +1 -0
  200. package/dist/esm/editor/QuillLoaderContext.d.ts +3 -0
  201. package/dist/esm/editor/QuillLoaderContext.d.ts.map +1 -0
  202. package/dist/esm/editor/QuillLoaderContext.js +8 -0
  203. package/dist/esm/editor/QuillLoaderContext.js.map +1 -0
  204. package/dist/esm/editor/QuillLoaderProvider.d.ts +8 -0
  205. package/dist/esm/editor/QuillLoaderProvider.d.ts.map +1 -0
  206. package/dist/esm/editor/QuillLoaderProvider.js +19 -0
  207. package/dist/esm/editor/QuillLoaderProvider.js.map +1 -0
  208. package/dist/esm/editor/QuillOptions.d.ts +9 -0
  209. package/dist/esm/editor/QuillOptions.d.ts.map +1 -0
  210. package/dist/esm/editor/QuillOptions.js +2 -0
  211. package/dist/esm/editor/QuillOptions.js.map +1 -0
  212. package/dist/esm/editor/SafeElement.d.ts +7 -0
  213. package/dist/esm/editor/SafeElement.d.ts.map +1 -0
  214. package/dist/esm/editor/SafeElement.js +2 -0
  215. package/dist/esm/editor/SafeElement.js.map +1 -0
  216. package/dist/esm/editor/SafeNode.d.ts +3 -0
  217. package/dist/esm/editor/SafeNode.d.ts.map +1 -0
  218. package/dist/esm/editor/SafeNode.js +2 -0
  219. package/dist/esm/editor/SafeNode.js.map +1 -0
  220. package/dist/esm/editor/applyHtml.d.ts +3 -0
  221. package/dist/esm/editor/applyHtml.d.ts.map +1 -0
  222. package/dist/esm/editor/applyHtml.js +23 -0
  223. package/dist/esm/editor/applyHtml.js.map +1 -0
  224. package/dist/esm/editor/characterCount.d.ts +3 -0
  225. package/dist/esm/editor/characterCount.d.ts.map +1 -0
  226. package/dist/esm/editor/characterCount.js +8 -0
  227. package/dist/esm/editor/characterCount.js.map +1 -0
  228. package/dist/esm/editor/htmlToPlainText.d.ts +2 -0
  229. package/dist/esm/editor/htmlToPlainText.d.ts.map +1 -0
  230. package/dist/esm/editor/htmlToPlainText.js +99 -0
  231. package/dist/esm/editor/htmlToPlainText.js.map +1 -0
  232. package/dist/esm/editor/index.d.ts +14 -0
  233. package/dist/esm/editor/index.d.ts.map +1 -0
  234. package/dist/esm/editor/index.js +14 -0
  235. package/dist/esm/editor/index.js.map +1 -0
  236. package/dist/esm/editor/renderSafeNodes.d.ts +4 -0
  237. package/dist/esm/editor/renderSafeNodes.d.ts.map +1 -0
  238. package/dist/esm/editor/renderSafeNodes.js +33 -0
  239. package/dist/esm/editor/renderSafeNodes.js.map +1 -0
  240. package/dist/esm/editor/safeUrls.d.ts +3 -0
  241. package/dist/esm/editor/safeUrls.d.ts.map +1 -0
  242. package/dist/esm/editor/safeUrls.js +41 -0
  243. package/dist/esm/editor/safeUrls.js.map +1 -0
  244. package/dist/esm/editor/sanitizeHtml.d.ts +3 -0
  245. package/dist/esm/editor/sanitizeHtml.d.ts.map +1 -0
  246. package/dist/esm/editor/sanitizeHtml.js +138 -0
  247. package/dist/esm/editor/sanitizeHtml.js.map +1 -0
  248. package/dist/esm/editor/serializeSafeNodes.d.ts +3 -0
  249. package/dist/esm/editor/serializeSafeNodes.d.ts.map +1 -0
  250. package/dist/esm/editor/serializeSafeNodes.js +31 -0
  251. package/dist/esm/editor/serializeSafeNodes.js.map +1 -0
  252. package/dist/esm/editor/toolbarLayout.d.ts +2 -0
  253. package/dist/esm/editor/toolbarLayout.d.ts.map +1 -0
  254. package/dist/esm/editor/toolbarLayout.js +33 -0
  255. package/dist/esm/editor/toolbarLayout.js.map +1 -0
  256. package/dist/esm/editor/useQuillEditor.d.ts +4 -0
  257. package/dist/esm/editor/useQuillEditor.d.ts.map +1 -0
  258. package/dist/esm/editor/useQuillEditor.js +135 -0
  259. package/dist/esm/editor/useQuillEditor.js.map +1 -0
  260. package/dist/esm/file/FileGallery.stories.d.ts +19 -0
  261. package/dist/esm/file/FileGallery.stories.d.ts.map +1 -0
  262. package/dist/esm/file/FileGallery.stories.js +23 -0
  263. package/dist/esm/file/FileGallery.stories.js.map +1 -0
  264. package/dist/esm/file/FileUploadContext.d.ts +7 -0
  265. package/dist/esm/file/FileUploadContext.d.ts.map +1 -0
  266. package/dist/esm/file/FileUploadContext.js +11 -0
  267. package/dist/esm/file/FileUploadContext.js.map +1 -0
  268. package/dist/esm/file/FileUploadHandler.d.ts +4 -0
  269. package/dist/esm/file/FileUploadHandler.d.ts.map +1 -0
  270. package/dist/esm/file/FileUploadHandler.js +2 -0
  271. package/dist/esm/file/FileUploadHandler.js.map +1 -0
  272. package/dist/esm/file/FileUploadHandlerProvider.d.ts +9 -0
  273. package/dist/esm/file/FileUploadHandlerProvider.d.ts.map +1 -0
  274. package/dist/esm/file/FileUploadHandlerProvider.js +23 -0
  275. package/dist/esm/file/FileUploadHandlerProvider.js.map +1 -0
  276. package/dist/esm/file/FileUploadMode.d.ts +6 -0
  277. package/dist/esm/file/FileUploadMode.d.ts.map +1 -0
  278. package/dist/esm/file/FileUploadMode.js +11 -0
  279. package/dist/esm/file/FileUploadMode.js.map +1 -0
  280. package/dist/esm/file/PrimeFileUpload.d.ts +3 -0
  281. package/dist/esm/file/PrimeFileUpload.d.ts.map +1 -0
  282. package/dist/esm/file/PrimeFileUpload.js +159 -0
  283. package/dist/esm/file/PrimeFileUpload.js.map +1 -0
  284. package/dist/esm/file/UploadStatus.d.ts +5 -0
  285. package/dist/esm/file/UploadStatus.d.ts.map +1 -0
  286. package/dist/esm/file/UploadStatus.js +2 -0
  287. package/dist/esm/file/UploadStatus.js.map +1 -0
  288. package/dist/esm/file/UploadTarget.d.ts +8 -0
  289. package/dist/esm/file/UploadTarget.d.ts.map +1 -0
  290. package/dist/esm/file/UploadTarget.js +2 -0
  291. package/dist/esm/file/UploadTarget.js.map +1 -0
  292. package/dist/esm/file/index.d.ts +7 -0
  293. package/dist/esm/file/index.d.ts.map +1 -0
  294. package/dist/esm/file/index.js +7 -0
  295. package/dist/esm/file/index.js.map +1 -0
  296. package/dist/esm/file/postFiles.d.ts +2 -0
  297. package/dist/esm/file/postFiles.d.ts.map +1 -0
  298. package/dist/esm/file/postFiles.js +29 -0
  299. package/dist/esm/file/postFiles.js.map +1 -0
  300. package/dist/esm/file/resolveUploadTarget.d.ts +3 -0
  301. package/dist/esm/file/resolveUploadTarget.d.ts.map +1 -0
  302. package/dist/esm/file/resolveUploadTarget.js +66 -0
  303. package/dist/esm/file/resolveUploadTarget.js.map +1 -0
  304. package/dist/esm/file/useUploadSession.d.ts +10 -0
  305. package/dist/esm/file/useUploadSession.d.ts.map +1 -0
  306. package/dist/esm/file/useUploadSession.js +60 -0
  307. package/dist/esm/file/useUploadSession.js.map +1 -0
  308. package/dist/esm/form/FormGallery.stories.d.ts.map +1 -1
  309. package/dist/esm/form/FormGallery.stories.js +1 -0
  310. package/dist/esm/form/FormGallery.stories.js.map +1 -1
  311. package/dist/esm/index.d.ts +2 -0
  312. package/dist/esm/index.d.ts.map +1 -1
  313. package/dist/esm/index.js +16 -1
  314. package/dist/esm/index.js.map +1 -1
  315. package/dist/esm/legacyEnumerationChoices.d.ts +3 -0
  316. package/dist/esm/legacyEnumerationChoices.d.ts.map +1 -0
  317. package/dist/esm/legacyEnumerationChoices.js +37 -0
  318. package/dist/esm/legacyEnumerationChoices.js.map +1 -0
  319. package/dist/esm/loadQuill/index.d.ts +3 -0
  320. package/dist/esm/loadQuill/index.d.ts.map +1 -0
  321. package/dist/esm/loadQuill/index.js +20 -0
  322. package/dist/esm/loadQuill/index.js.map +1 -0
  323. package/dist/esm/primeReactComponents.d.ts.map +1 -1
  324. package/dist/esm/primeReactComponents.js +10 -0
  325. package/dist/esm/primeReactComponents.js.map +1 -1
  326. package/dist/esm/primeReactDescriptors.d.ts.map +1 -1
  327. package/dist/esm/primeReactDescriptors.js +243 -0
  328. package/dist/esm/primeReactDescriptors.js.map +1 -1
  329. package/dist/esm/primeReactPackage.js +4 -0
  330. package/dist/esm/primeReactPackage.js.map +1 -1
  331. package/dist/esm/resolveEnumeration.d.ts +3 -0
  332. package/dist/esm/resolveEnumeration.d.ts.map +1 -0
  333. package/dist/esm/resolveEnumeration.js +53 -0
  334. package/dist/esm/resolveEnumeration.js.map +1 -0
  335. package/dist/esm/treeNodes.d.ts +2 -1
  336. package/dist/esm/treeNodes.d.ts.map +1 -1
  337. package/dist/esm/treeNodes.js +42 -8
  338. package/dist/esm/treeNodes.js.map +1 -1
  339. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  340. package/dist/esm/useResettingState.d.ts +3 -0
  341. package/dist/esm/useResettingState.d.ts.map +1 -0
  342. package/dist/esm/useResettingState.js +45 -0
  343. package/dist/esm/useResettingState.js.map +1 -0
  344. package/editor/PrimeEditor.tsx +83 -0
  345. package/editor/QuillConstructor.ts +8 -0
  346. package/editor/QuillEditorOptions.ts +29 -0
  347. package/editor/QuillEditorState.ts +14 -0
  348. package/editor/QuillInstance.ts +27 -0
  349. package/editor/QuillLoader.ts +13 -0
  350. package/editor/QuillLoaderContext.ts +8 -0
  351. package/editor/QuillLoaderProvider.tsx +24 -0
  352. package/editor/QuillOptions.ts +10 -0
  353. package/editor/SafeElement.ts +15 -0
  354. package/editor/SafeNode.ts +7 -0
  355. package/editor/applyHtml.ts +21 -0
  356. package/editor/characterCount.ts +9 -0
  357. package/editor/htmlToPlainText.ts +74 -0
  358. package/editor/index.ts +16 -0
  359. package/editor/renderSafeNodes.tsx +26 -0
  360. package/editor/safeUrls.ts +43 -0
  361. package/editor/sanitizeHtml.ts +94 -0
  362. package/editor/serializeSafeNodes.ts +26 -0
  363. package/editor/toolbarLayout.ts +10 -0
  364. package/editor/useQuillEditor.ts +114 -0
  365. package/file/FileGallery.stories.tsx +32 -0
  366. package/file/FileUploadContext.ts +17 -0
  367. package/file/FileUploadHandler.ts +15 -0
  368. package/file/FileUploadHandlerProvider.tsx +33 -0
  369. package/file/FileUploadMode.ts +9 -0
  370. package/file/PrimeFileUpload.tsx +129 -0
  371. package/file/UploadStatus.ts +9 -0
  372. package/file/UploadTarget.ts +12 -0
  373. package/file/index.ts +9 -0
  374. package/file/postFiles.ts +23 -0
  375. package/file/resolveUploadTarget.ts +61 -0
  376. package/file/useUploadSession.ts +67 -0
  377. package/form/FormGallery.stories.tsx +1 -0
  378. package/index.ts +2 -0
  379. package/legacyEnumerationChoices.ts +35 -0
  380. package/loadQuill/index.ts +21 -0
  381. package/package.json +17 -7
  382. package/primeReactComponents.ts +8 -0
  383. package/primeReactDescriptors.ts +63 -0
  384. package/primeReactPackage.ts +2 -2
  385. package/primeReactTheme.css +69 -0
  386. package/resolveEnumeration.ts +50 -0
  387. package/treeNodes.ts +44 -6
  388. package/useResettingState.ts +34 -0
@@ -0,0 +1,3 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ export declare function useResettingState<TState>(source: unknown, initial: () => TState): [TState, Dispatch<SetStateAction<TState>>];
3
+ //# sourceMappingURL=useResettingState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResettingState.d.ts","sourceRoot":"","sources":["../../useResettingState.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAyB,MAAM,OAAO,CAAC;AAgBxE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAc5H"}
@@ -0,0 +1,45 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { useStructuralValue } from './useStructuralValue.js';
3
+
4
+ // Copyright (c) Cratis. All rights reserved.
5
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
6
+ /**
7
+ * State that starts from authored data and starts over when that data really changes.
8
+ *
9
+ * A control's selection or expansion is runtime state the user changes, yet it is also authored on the
10
+ * element. Reading the authored value once (`useState(() => authored)`) ignores a later edit of the document;
11
+ * resetting on every render discards what the user did. This does neither: the state follows `source` when
12
+ * its content changes, compared structurally because every document edit clones the data, and is otherwise
13
+ * left alone - so an unrelated property edit does not undo the user's choice.
14
+ *
15
+ * @param source The authored data the state derives from.
16
+ * @param initial Derives the starting state from the authored data.
17
+ * @returns The state and its setter, like `useState`.
18
+ */ function useResettingState(source, initial) {
19
+ const stable = useStructuralValue(source);
20
+ const [held, setHeld] = useState(()=>({
21
+ source: stable,
22
+ value: initial()
23
+ }));
24
+ let current = held;
25
+ if (held.source !== stable) {
26
+ current = {
27
+ source: stable,
28
+ value: initial()
29
+ };
30
+ setHeld(current);
31
+ }
32
+ const update = useCallback((action)=>{
33
+ setHeld((previous)=>({
34
+ source: previous.source,
35
+ value: typeof action === 'function' ? action(previous.value) : action
36
+ }));
37
+ }, []);
38
+ return [
39
+ current.value,
40
+ update
41
+ ];
42
+ }
43
+
44
+ export { useResettingState };
45
+ //# sourceMappingURL=useResettingState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResettingState.js","sources":["../../useResettingState.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Dispatch, SetStateAction, useCallback, useState } from 'react';\nimport { useStructuralValue } from './useStructuralValue';\n\n/**\n * State that starts from authored data and starts over when that data really changes.\n *\n * A control's selection or expansion is runtime state the user changes, yet it is also authored on the\n * element. Reading the authored value once (`useState(() => authored)`) ignores a later edit of the document;\n * resetting on every render discards what the user did. This does neither: the state follows `source` when\n * its content changes, compared structurally because every document edit clones the data, and is otherwise\n * left alone - so an unrelated property edit does not undo the user's choice.\n *\n * @param source The authored data the state derives from.\n * @param initial Derives the starting state from the authored data.\n * @returns The state and its setter, like `useState`.\n */\nexport function useResettingState<TState>(source: unknown, initial: () => TState): [TState, Dispatch<SetStateAction<TState>>] {\n const stable = useStructuralValue(source);\n const [held, setHeld] = useState(() => ({ source: stable, value: initial() }));\n let current = held;\n if (held.source !== stable) {\n current = { source: stable, value: initial() };\n setHeld(current);\n }\n\n const update = useCallback((action: SetStateAction<TState>) => {\n setHeld(previous => ({ source: previous.source, value: typeof action === 'function' ? (action as (state: TState) => TState)(previous.value) : action }));\n }, []);\n\n return [current.value, update];\n}\n"],"names":["useResettingState","source","initial","stable","useStructuralValue","held","setHeld","useState","value","current","update","useCallback","action","previous"],"mappings":";;;AAAA;AACA;AAKA;;;;;;;;;;;;AAYC,IACM,SAASA,iBAAAA,CAA0BC,MAAe,EAAEC,OAAqB,EAAA;AAC5E,IAAA,MAAMC,SAASC,kBAAAA,CAAmBH,MAAAA,CAAAA;AAClC,IAAA,MAAM,CAACI,IAAAA,EAAMC,OAAAA,CAAQ,GAAGC,QAAAA,CAAS,KAAO;YAAEN,MAAAA,EAAQE,MAAAA;YAAQK,KAAAA,EAAON,OAAAA;SAAU,CAAA,CAAA;AAC3E,IAAA,IAAIO,OAAAA,GAAUJ,IAAAA;IACd,IAAIA,IAAAA,CAAKJ,MAAM,KAAKE,MAAAA,EAAQ;QACxBM,OAAAA,GAAU;YAAER,MAAAA,EAAQE,MAAAA;YAAQK,KAAAA,EAAON,OAAAA;AAAU,SAAA;QAC7CI,OAAAA,CAAQG,OAAAA,CAAAA;AACZ,IAAA;IAEA,MAAMC,MAAAA,GAASC,YAAY,CAACC,MAAAA,GAAAA;QACxBN,OAAAA,CAAQO,CAAAA,YAAa;AAAEZ,gBAAAA,MAAAA,EAAQY,SAASZ,MAAM;AAAEO,gBAAAA,KAAAA,EAAO,OAAOI,MAAAA,KAAW,UAAA,GAAa,MAACA,CAAqCC,QAAAA,CAASL,KAAK,CAAA,GAAII;aAAO,CAAA,CAAA;AACzJ,IAAA,CAAA,EAAG,EAAE,CAAA;IAEL,OAAO;AAACH,QAAAA,OAAAA,CAAQD,KAAK;AAAEE,QAAAA;AAAO,KAAA;AAClC;;"}
@@ -0,0 +1,83 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { useContext, useEffect, useMemo, useRef, useState } from 'react';
5
+ import { RegisteredComponentProps } from '@cratis/scene.react';
6
+ import { booleanProperty, stringProperty } from '../properties';
7
+ import { QuillLoaderContext } from './QuillLoaderContext';
8
+ import { htmlToPlainText } from './htmlToPlainText';
9
+ import { renderSafeNodes } from './renderSafeNodes';
10
+ import { sanitizeHtml } from './sanitizeHtml';
11
+ import { useQuillEditor } from './useQuillEditor';
12
+
13
+ /**
14
+ * The `PrimeReact:editor` rich-text control.
15
+ *
16
+ * Authored HTML is untrusted data and is never injected as markup. Everywhere it is shown it first goes
17
+ * through an allowlist (see {@link sanitizeHtml}): into Quill as a Delta, or into the read-only view as React
18
+ * elements. Where there is no DOM - server rendering, and the first client render so hydration matches - it is
19
+ * shown as plain text. `element.properties.value` itself is never changed.
20
+ *
21
+ * Editing needs Quill, which this package does not import. A host provides it with `QuillLoaderProvider`
22
+ * (the loader `loadQuill` lives in the `@cratis/scene.primereact/quill` entry point). Without a loader, or if
23
+ * loading fails, the content stays visible read-only and a status says why; the control never pretends to be
24
+ * editable.
25
+ *
26
+ * `readOnly: true` and `isEnabled: false` both stop editing. Edits are runtime state: `interactions.onChange`
27
+ * is called when the user edits, but the new HTML is not written back to the document or a binding.
28
+ */
29
+ export function PrimeEditor({ element, interactions }: RegisteredComponentProps) {
30
+ const loader = useContext(QuillLoaderContext);
31
+ const host = useRef<HTMLDivElement>(null);
32
+ const interactionReference = useRef(interactions);
33
+ interactionReference.current = interactions;
34
+ const [mounted, setMounted] = useState(false);
35
+ const html = stringProperty(element, 'value', '');
36
+ const readOnly = booleanProperty(element, 'readOnly', false);
37
+ const editable = !readOnly && element.isEnabled;
38
+ const ariaLabel = stringProperty(element, 'ariaLabel', 'Rich text editor');
39
+
40
+ useEffect(() => setMounted(true), []);
41
+
42
+ const editor = useQuillEditor({
43
+ loader,
44
+ host,
45
+ html,
46
+ showHeader: booleanProperty(element, 'showHeader', true),
47
+ editable,
48
+ placeholder: stringProperty(element, 'placeholder'),
49
+ ariaLabel,
50
+ onUserChange: () => interactionReference.current?.onChange?.(),
51
+ });
52
+
53
+ const readOnlyView = useMemo(() => {
54
+ if (!mounted || typeof DOMParser === 'undefined') return htmlToPlainText(html);
55
+ return renderSafeNodes(sanitizeHtml(html));
56
+ }, [mounted, html]);
57
+
58
+ const status = !editor.ready ? undefined : editable ? `${editor.characters} characters` : readOnly ? 'Read only' : 'Disabled';
59
+
60
+ return (
61
+ <section data-scene-id={element.id} data-scene-component='editor' onClick={interactions?.onClick} onDoubleClick={interactions?.onDoubleClick}>
62
+ <div ref={host} />
63
+ {!editor.ready && (
64
+ <div
65
+ key={typeof readOnlyView}
66
+ role='textbox'
67
+ tabIndex={0}
68
+ aria-multiline='true'
69
+ aria-readonly='true'
70
+ aria-label={ariaLabel}
71
+ data-scene-part='read-only-content'
72
+ style={typeof readOnlyView === 'string' ? { whiteSpace: 'pre-wrap' } : undefined}>
73
+ {readOnlyView}
74
+ </div>
75
+ )}
76
+ {editor.failure !== undefined && <p role='alert' data-scene-state='unavailable'>{`Rich-text editing could not be loaded: ${editor.failure}`}</p>}
77
+ {!editor.ready && editor.failure === undefined && loader === undefined && (
78
+ <p role='status' data-scene-state='read-only'>Rich-text editing is not available in this application. The content is shown read-only.</p>
79
+ )}
80
+ {status !== undefined && <output aria-live='polite'>{status}</output>}
81
+ </section>
82
+ );
83
+ }
@@ -0,0 +1,8 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { QuillInstance } from './QuillInstance';
5
+ import { QuillOptions } from './QuillOptions';
6
+
7
+ /** Quill's constructor, as this package calls it. */
8
+ export type QuillConstructor = new (container: HTMLElement, options: QuillOptions) => QuillInstance;
@@ -0,0 +1,29 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { RefObject } from 'react';
5
+ import { QuillLoader } from './QuillLoader';
6
+
7
+ /** What {@link useQuillEditor} builds an editor from. */
8
+ export interface QuillEditorOptions {
9
+ /** Loads Quill. Without one no editor is built. */
10
+ loader: QuillLoader | undefined;
11
+
12
+ /** The element the editor is built inside. React must render it with no children. */
13
+ host: RefObject<HTMLElement | null>;
14
+
15
+ /** The authored HTML. A change in it replaces the content; no other change does. */
16
+ html: string;
17
+
18
+ /** Whether the toolbar is shown. It is fixed when Quill is constructed, so a change rebuilds the editor. */
19
+ showHeader: boolean;
20
+
21
+ /** Whether the user can edit: not read only and not disabled. */
22
+ editable: boolean;
23
+
24
+ placeholder: string | undefined;
25
+ ariaLabel: string;
26
+
27
+ /** Called when the user, not the document, changed the content. */
28
+ onUserChange(): void;
29
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /** What {@link useQuillEditor} reports about the editor it manages. */
5
+ export interface QuillEditorState {
6
+ /** Whether the editor is built and showing the content. */
7
+ ready: boolean;
8
+
9
+ /** Why the editor could not be built, once loading failed. */
10
+ failure: string | undefined;
11
+
12
+ /** The number of characters of text, `0` until ready. */
13
+ characters: number;
14
+ }
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /**
5
+ * The part of a Quill editor this package uses.
6
+ *
7
+ * It is declared here instead of imported from `quill` so that this package's public types never name a
8
+ * module the consumer may not have installed.
9
+ */
10
+ export interface QuillInstance {
11
+ /** The editable surface. */
12
+ root: HTMLElement;
13
+
14
+ clipboard: {
15
+ /** Converts HTML to a Delta. Quill drops formats it does not know while doing so. */
16
+ convert(content: { html?: string; text?: string }): unknown;
17
+ };
18
+
19
+ /** Replaces the content. `'silent'` suppresses the `text-change` event. */
20
+ setContents(delta: unknown, source?: 'user' | 'api' | 'silent'): unknown;
21
+
22
+ enable(enabled?: boolean): void;
23
+ getText(): string;
24
+ getModule(name: string): unknown;
25
+ on(event: 'text-change', handler: (delta: unknown, previous: unknown, source: string) => void): unknown;
26
+ off(event: 'text-change', handler: (delta: unknown, previous: unknown, source: string) => void): unknown;
27
+ }
@@ -0,0 +1,13 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { QuillConstructor } from './QuillConstructor';
5
+
6
+ /**
7
+ * Loads Quill when the first editor needs it.
8
+ *
9
+ * It resolves to Quill's constructor, or to the module that has it as its default export, so a dynamic
10
+ * import of the quill package is a loader. The host provides it with `QuillLoaderProvider`; the package
11
+ * never imports that package itself, so a host that renders no rich text needs no `quill` to build.
12
+ */
13
+ export type QuillLoader = () => Promise<QuillConstructor | { default: QuillConstructor }>;
@@ -0,0 +1,8 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { createContext } from 'react';
5
+ import { QuillLoader } from './QuillLoader';
6
+
7
+ /** Without a provider there is no loader, and the editor shows its content read-only. */
8
+ export const QuillLoaderContext = createContext<QuillLoader | undefined>(undefined);
@@ -0,0 +1,24 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { ReactNode } from 'react';
5
+ import { QuillLoader } from './QuillLoader';
6
+ import { QuillLoaderContext } from './QuillLoaderContext';
7
+
8
+ export interface QuillLoaderProviderProps {
9
+ /** Loads Quill. `loadQuill` from `@cratis/scene.primereact/quill` is the ready-made one. */
10
+ loader: QuillLoader;
11
+
12
+ /** The surface whose editors the loader serves. */
13
+ children: ReactNode;
14
+ }
15
+
16
+ /**
17
+ * Makes editable rich text available to the `editor` controls inside it.
18
+ *
19
+ * The loader must be a stable reference - define it at module level or memoize it - because the editor
20
+ * rebuilds when it changes.
21
+ */
22
+ export function QuillLoaderProvider({ loader, children }: QuillLoaderProviderProps) {
23
+ return <QuillLoaderContext.Provider value={loader}>{children}</QuillLoaderContext.Provider>;
24
+ }
@@ -0,0 +1,10 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /** The options this package passes when it constructs Quill. */
5
+ export interface QuillOptions {
6
+ theme: 'snow';
7
+ modules: { toolbar: (string | Record<string, unknown>)[][] | false };
8
+ placeholder?: string;
9
+ readOnly: boolean;
10
+ }
@@ -0,0 +1,15 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { SafeNode } from './SafeNode';
5
+
6
+ /** An element of authored HTML that the allowlist kept, with only the attributes the allowlist kept. */
7
+ export interface SafeElement {
8
+ /** A tag from the allowlist, lower case. */
9
+ tag: string;
10
+
11
+ /** The kept attributes, already validated. */
12
+ attributes: Record<string, string>;
13
+
14
+ children: SafeNode[];
15
+ }
@@ -0,0 +1,7 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import type { SafeElement } from './SafeElement';
5
+
6
+ /** Authored HTML after the allowlist: text, or an element that is safe to render. */
7
+ export type SafeNode = string | SafeElement;
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { QuillInstance } from './QuillInstance';
5
+ import { sanitizeHtml } from './sanitizeHtml';
6
+ import { serializeSafeNodes } from './serializeSafeNodes';
7
+
8
+ /**
9
+ * Puts authored HTML into a Quill editor, silently.
10
+ *
11
+ * The HTML first goes through the allowlist, and what is left is converted by Quill's clipboard into a Delta
12
+ * that `setContents` applies. The authored string is never assigned to `innerHTML`, so nothing in it can run;
13
+ * and because the change is `'silent'` it raises no `text-change`, so loading a document is not reported as
14
+ * the user editing it.
15
+ *
16
+ * @param quill The editor.
17
+ * @param html The authored HTML.
18
+ */
19
+ export function applyHtml(quill: QuillInstance, html: string): void {
20
+ quill.setContents(quill.clipboard.convert({ html: serializeSafeNodes(sanitizeHtml(html)) }), 'silent');
21
+ }
@@ -0,0 +1,9 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { QuillInstance } from './QuillInstance';
5
+
6
+ /** The number of characters of text in the editor, not counting the newline Quill always ends with. */
7
+ export function characterCount(quill: QuillInstance): number {
8
+ return Math.max(quill.getText().length - 1, 0);
9
+ }
@@ -0,0 +1,74 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /** Elements dropped together with their content, as `sanitizeHtml` does. */
5
+ const discardedWithContent = new Set(['script', 'style', 'template', 'noscript', 'iframe', 'object', 'embed', 'svg', 'math', 'textarea', 'title', 'head']);
6
+
7
+ /** Elements after which a line break reads naturally. */
8
+ const breaking = new Set(['p', 'div', 'br', 'hr', 'li', 'tr', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
9
+
10
+ const namedEntities: Record<string, string> = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: ' ' };
11
+
12
+ /**
13
+ * Reads authored HTML as plain text, without a DOM.
14
+ *
15
+ * This is what server rendering shows, where there is no `DOMParser` to build safe markup with. The result
16
+ * is only ever rendered as text, which React escapes, so it cannot carry markup however the input is built.
17
+ * It scans once, left to right: tags are dropped, script, style and similar elements go with their content,
18
+ * and block elements become line breaks.
19
+ *
20
+ * @param html The authored HTML.
21
+ * @returns The text, with line breaks between blocks.
22
+ */
23
+ export function htmlToPlainText(html: string): string {
24
+ const lower = html.toLowerCase();
25
+ let text = '';
26
+ let position = 0;
27
+ while (position < html.length) {
28
+ const open = html.indexOf('<', position);
29
+ if (open === -1) {
30
+ text += html.slice(position);
31
+ break;
32
+ }
33
+
34
+ text += html.slice(position, open);
35
+ if (!/[a-z/!?]/i.test(html[open + 1] ?? '')) {
36
+ text += '<';
37
+ position = open + 1;
38
+ continue;
39
+ }
40
+
41
+ if (lower.startsWith('<!--', open)) {
42
+ const close = html.indexOf('-->', open + 4);
43
+ position = close === -1 ? html.length : close + 3;
44
+ continue;
45
+ }
46
+
47
+ const close = html.indexOf('>', open);
48
+ if (close === -1) {
49
+ text += html.slice(open);
50
+ break;
51
+ }
52
+
53
+ const name = /^<\/?([a-z][a-z0-9]*)/.exec(lower.slice(open, close + 1))?.[1];
54
+ position = close + 1;
55
+ if (name === undefined) continue;
56
+ if (lower[open + 1] !== '/' && discardedWithContent.has(name)) {
57
+ const end = lower.indexOf(`</${name}`, position);
58
+ const endClose = end === -1 ? -1 : html.indexOf('>', end);
59
+ position = endClose === -1 ? html.length : endClose + 1;
60
+ } else if (breaking.has(name) && (lower[open + 1] === '/' || name === 'br' || name === 'hr')) {
61
+ text += '\n';
62
+ }
63
+ }
64
+
65
+ return decodeEntities(text).replace(/\n{3,}/g, '\n\n').trim();
66
+ }
67
+
68
+ function decodeEntities(text: string): string {
69
+ return text.replace(/&(#x[0-9a-f]{1,6}|#[0-9]{1,7}|[a-z]+);/gi, (match, entity: string) => {
70
+ if (entity[0] !== '#') return namedEntities[entity.toLowerCase()] ?? match;
71
+ const code = entity[1].toLowerCase() === 'x' ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);
72
+ return code > 0 && code <= 0x10ffff && !(code >= 0xd800 && code <= 0xdfff) ? String.fromCodePoint(code) : match;
73
+ });
74
+ }
@@ -0,0 +1,16 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './PrimeEditor';
5
+ export * from './QuillConstructor';
6
+ export * from './QuillInstance';
7
+ export * from './QuillLoader';
8
+ export * from './QuillLoaderProvider';
9
+ export * from './QuillOptions';
10
+ export * from './htmlToPlainText';
11
+ export * from './renderSafeNodes';
12
+ export * from './SafeElement';
13
+ export * from './SafeNode';
14
+ export * from './safeUrls';
15
+ export * from './sanitizeHtml';
16
+ export * from './serializeSafeNodes';
@@ -0,0 +1,26 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { createElement, ReactNode } from 'react';
5
+ import { SafeNode } from './SafeNode';
6
+
7
+ const voidTags = new Set(['br', 'hr', 'img']);
8
+
9
+ /**
10
+ * Renders safe nodes as React elements.
11
+ *
12
+ * Nothing is ever injected as markup: each node becomes an element created from the allowlisted tag name with
13
+ * the validated attributes, and text goes through React's escaping. Links are made `noopener noreferrer` and
14
+ * open in the same context.
15
+ *
16
+ * @param nodes The nodes `sanitizeHtml` returned.
17
+ */
18
+ export function renderSafeNodes(nodes: SafeNode[]): ReactNode[] {
19
+ return nodes.map((node, index) => {
20
+ if (typeof node === 'string') return node;
21
+ const { class: className, ...attributes } = node.attributes;
22
+ const properties: Record<string, string | undefined> = { key: String(index), className, ...attributes };
23
+ if (node.tag === 'a') properties.rel = 'noopener noreferrer';
24
+ return createElement(node.tag, properties, ...(voidTags.has(node.tag) ? [] : renderSafeNodes(node.children)));
25
+ });
26
+ }
@@ -0,0 +1,43 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /** Schemes a link may use. Anything else, `javascript:` and `data:` above all, is dropped. */
5
+ const linkProtocols = new Set(['http:', 'https:', 'mailto:', 'tel:']);
6
+
7
+ /** The only images that are kept: small raster data URLs. A remote image is a request made just by rendering. */
8
+ const rasterDataUrl = /^data:image\/(png|jpeg|gif|webp);base64,[a-z0-9+/]+={0,2}$/i;
9
+
10
+ /** Resolved against, to learn which scheme a browser would act on. It is never sent to. */
11
+ const placeholderOrigin = 'https://editor.invalid';
12
+
13
+ /**
14
+ * Returns a link target a browser may follow, or `undefined`.
15
+ *
16
+ * Browsers strip tabs and newlines from inside a scheme, so the address is judged by what `URL` resolves it to
17
+ * and not by its text: `java\tscript:` is a `javascript:` link. Relative links and fragments resolve against
18
+ * an http origin and are kept.
19
+ *
20
+ * @param value The authored `href`.
21
+ */
22
+ export function safeLinkUrl(value: string): string | undefined {
23
+ const trimmed = value.trim();
24
+ if (trimmed === '') return undefined;
25
+ try {
26
+ return linkProtocols.has(new URL(trimmed, placeholderOrigin).protocol) ? trimmed : undefined;
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Returns an image source that may be rendered, or `undefined`.
34
+ *
35
+ * Only base64 PNG, JPEG, GIF and WebP data URLs pass. SVG is refused because it can carry script, and an
36
+ * `http(s)` image is refused because rendering it would contact a server the author named.
37
+ *
38
+ * @param value The authored `src`.
39
+ */
40
+ export function safeImageUrl(value: string): string | undefined {
41
+ const trimmed = value.trim();
42
+ return rasterDataUrl.test(trimmed) ? trimmed : undefined;
43
+ }
@@ -0,0 +1,94 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { SafeElement } from './SafeElement';
5
+ import { SafeNode } from './SafeNode';
6
+ import { safeImageUrl, safeLinkUrl } from './safeUrls';
7
+
8
+ /** Elements that are kept. Every other element is unwrapped: its children stay, the element does not. */
9
+ const keptTags = new Set([
10
+ 'p', 'div', 'span', 'br', 'hr', 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', 'sub', 'sup', 'code', 'pre', 'blockquote',
11
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img',
12
+ ]);
13
+
14
+ /** Elements whose content is never text to show - script, style, embedded documents and form controls. */
15
+ const discardedWithContent = new Set([
16
+ 'script', 'style', 'template', 'noscript', 'iframe', 'frame', 'frameset', 'object', 'embed', 'applet', 'svg', 'math',
17
+ 'audio', 'video', 'canvas', 'textarea', 'select', 'option', 'button', 'input', 'form', 'head', 'title', 'meta', 'link', 'base',
18
+ ]);
19
+
20
+ /** Quill's own formatting classes. They name CSS rules, so a fixed vocabulary is safe and anything else is not kept. */
21
+ const formattingClass = /^ql-(align-(center|right|justify)|indent-[1-8]|size-(small|large|huge)|font-(serif|monospace)|direction-rtl)$/;
22
+
23
+ const listKinds = new Set(['bullet', 'ordered', 'checked', 'unchecked']);
24
+
25
+ /** Deeper than any real document nests. Anything below is flattened to its text. */
26
+ const maximumDepth = 64;
27
+
28
+ /**
29
+ * Reduces authored HTML to the part that is safe to render.
30
+ *
31
+ * It parses with `DOMParser`, which builds an inert document: nothing in it loads, runs or paints while it is
32
+ * walked. The walk then keeps only elements from an allowlist and, on those, only the attributes listed
33
+ * here - a validated `href` or image `src`, Quill's formatting classes and list kind. Event handlers,
34
+ * `style`, `srcdoc`, unknown elements' attributes and every URL that is not allowed are dropped, and script,
35
+ * style, SVG, frames and form controls are removed together with their content.
36
+ *
37
+ * The result is plain data. Render it with `renderSafeNodes` or serialize it with `serializeSafeNodes`; the
38
+ * authored string itself is never put into the document.
39
+ *
40
+ * @param html The authored HTML.
41
+ * @returns The safe nodes.
42
+ * @throws When there is no `DOMParser`. Use `htmlToPlainText` where there is no DOM.
43
+ */
44
+ export function sanitizeHtml(html: string): SafeNode[] {
45
+ if (typeof DOMParser === 'undefined') throw new Error('Sanitizing HTML needs a DOM. Use htmlToPlainText where there is none.');
46
+ return sanitizeChildren(new DOMParser().parseFromString(html, 'text/html').body, 0);
47
+ }
48
+
49
+ function sanitizeChildren(parent: Node, depth: number): SafeNode[] {
50
+ const nodes: SafeNode[] = [];
51
+ for (const child of Array.from(parent.childNodes)) {
52
+ if (child.nodeType === Node.TEXT_NODE) {
53
+ if (child.textContent !== null && child.textContent !== '') nodes.push(child.textContent);
54
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
55
+ nodes.push(...sanitizeElement(child as Element, depth));
56
+ }
57
+ }
58
+ return nodes;
59
+ }
60
+
61
+ function sanitizeElement(element: Element, depth: number): SafeNode[] {
62
+ const tag = element.tagName.toLowerCase();
63
+ if (discardedWithContent.has(tag)) return [];
64
+ if (depth >= maximumDepth) return [element.textContent ?? ''];
65
+
66
+ const children = sanitizeChildren(element, depth + 1);
67
+ if (!keptTags.has(tag)) return children;
68
+
69
+ const attributes = keptAttributes(tag, element);
70
+ if (tag === 'img' && attributes.src === undefined) return [];
71
+ if (tag === 'a' && attributes.href === undefined) return children;
72
+ return [{ tag, attributes, children } satisfies SafeElement];
73
+ }
74
+
75
+ function keptAttributes(tag: string, element: Element): Record<string, string> {
76
+ const attributes: Record<string, string> = {};
77
+
78
+ const href = tag === 'a' ? safeLinkUrl(element.getAttribute('href') ?? '') : undefined;
79
+ if (href !== undefined) attributes.href = href;
80
+
81
+ const source = tag === 'img' ? safeImageUrl(element.getAttribute('src') ?? '') : undefined;
82
+ if (source !== undefined) {
83
+ attributes.src = source;
84
+ attributes.alt = element.getAttribute('alt') ?? '';
85
+ }
86
+
87
+ const classes = (element.getAttribute('class') ?? '').split(/\s+/).filter(name => formattingClass.test(name));
88
+ if (classes.length > 0) attributes.class = classes.join(' ');
89
+
90
+ const listKind = element.getAttribute('data-list');
91
+ if (tag === 'li' && listKind !== null && listKinds.has(listKind)) attributes['data-list'] = listKind;
92
+
93
+ return attributes;
94
+ }
@@ -0,0 +1,26 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { SafeNode } from './SafeNode';
5
+
6
+ const voidTags = new Set(['br', 'hr', 'img']);
7
+
8
+ const escapes: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' };
9
+
10
+ const escape = (text: string) => text.replace(/[&<>"]/g, character => escapes[character]);
11
+
12
+ /**
13
+ * Writes safe nodes back out as an HTML string, for an editor that reads HTML.
14
+ *
15
+ * Text and attribute values are escaped, and only tags and attributes the allowlist produced are written, so
16
+ * the string can contain nothing the allowlist did not already accept.
17
+ *
18
+ * @param nodes The nodes `sanitizeHtml` returned.
19
+ */
20
+ export function serializeSafeNodes(nodes: SafeNode[]): string {
21
+ return nodes.map(node => {
22
+ if (typeof node === 'string') return escape(node);
23
+ const attributes = Object.entries(node.attributes).map(([name, value]) => ` ${name}="${escape(value)}"`).join('');
24
+ return voidTags.has(node.tag) ? `<${node.tag}${attributes}>` : `<${node.tag}${attributes}>${serializeSafeNodes(node.children)}</${node.tag}>`;
25
+ }).join('');
26
+ }
@@ -0,0 +1,10 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /** The formats the toolbar offers. Content in other formats is still shown, but cannot be produced here. */
5
+ export const toolbarLayout: (string | Record<string, unknown>)[][] = [
6
+ ['bold', 'italic', 'underline'],
7
+ [{ header: [1, 2, 3, false] }],
8
+ [{ list: 'ordered' }, { list: 'bullet' }],
9
+ ['link'],
10
+ ];