@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.
- package/.storybook/preview.tsx +8 -1
- package/EnumerationResolution.ts +7 -0
- package/README.md +22 -12
- package/data/DataGallery.stories.tsx +6 -0
- package/data/DataScrollerRow.tsx +35 -0
- package/data/PrimeDataScroller.tsx +84 -0
- package/data/PrimeTreeTable.tsx +145 -0
- package/data/TreeTableCheckState.ts +9 -0
- package/data/TreeTableRow.ts +22 -0
- package/data/TreeTableRowView.tsx +86 -0
- package/data/TreeTableSelectionMode.ts +10 -0
- package/data/checkboxSelection.ts +92 -0
- package/data/index.ts +4 -0
- package/data/isSerializedLegacyElement.ts +30 -0
- package/data/readableValue.ts +32 -0
- package/data/selectionKeysOf.ts +40 -0
- package/data/treeTableRows.ts +32 -0
- package/data/useIncrementalLoad.ts +36 -0
- package/data/useLoadOnReach.ts +31 -0
- package/data/useTreeGridNavigation.ts +81 -0
- package/dist/cjs/data/DataScrollerRow.js +50 -0
- package/dist/cjs/data/DataScrollerRow.js.map +1 -0
- package/dist/cjs/data/PrimeDataScroller.js +120 -0
- package/dist/cjs/data/PrimeDataScroller.js.map +1 -0
- package/dist/cjs/data/PrimeTreeTable.js +217 -0
- package/dist/cjs/data/PrimeTreeTable.js.map +1 -0
- package/dist/cjs/data/TreeTableCheckState.js +13 -0
- package/dist/cjs/data/TreeTableCheckState.js.map +1 -0
- package/dist/cjs/data/TreeTableRowView.js +76 -0
- package/dist/cjs/data/TreeTableRowView.js.map +1 -0
- package/dist/cjs/data/TreeTableSelectionMode.js +14 -0
- package/dist/cjs/data/TreeTableSelectionMode.js.map +1 -0
- package/dist/cjs/data/checkboxSelection.js +88 -0
- package/dist/cjs/data/checkboxSelection.js.map +1 -0
- package/dist/cjs/data/isSerializedLegacyElement.js +30 -0
- package/dist/cjs/data/isSerializedLegacyElement.js.map +1 -0
- package/dist/cjs/data/readableValue.js +31 -0
- package/dist/cjs/data/readableValue.js.map +1 -0
- package/dist/cjs/data/selectionKeysOf.js +40 -0
- package/dist/cjs/data/selectionKeysOf.js.map +1 -0
- package/dist/cjs/data/treeTableRows.js +42 -0
- package/dist/cjs/data/treeTableRows.js.map +1 -0
- package/dist/cjs/data/useIncrementalLoad.js +46 -0
- package/dist/cjs/data/useIncrementalLoad.js.map +1 -0
- package/dist/cjs/data/useLoadOnReach.js +42 -0
- package/dist/cjs/data/useLoadOnReach.js.map +1 -0
- package/dist/cjs/data/useTreeGridNavigation.js +75 -0
- package/dist/cjs/data/useTreeGridNavigation.js.map +1 -0
- package/dist/cjs/editor/PrimeEditor.js +96 -0
- package/dist/cjs/editor/PrimeEditor.js.map +1 -0
- package/dist/cjs/editor/QuillLoaderContext.js +10 -0
- package/dist/cjs/editor/QuillLoaderContext.js.map +1 -0
- package/dist/cjs/editor/QuillLoaderProvider.js +21 -0
- package/dist/cjs/editor/QuillLoaderProvider.js.map +1 -0
- package/dist/cjs/editor/applyHtml.js +25 -0
- package/dist/cjs/editor/applyHtml.js.map +1 -0
- package/dist/cjs/editor/characterCount.js +10 -0
- package/dist/cjs/editor/characterCount.js.map +1 -0
- package/dist/cjs/editor/htmlToPlainText.js +101 -0
- package/dist/cjs/editor/htmlToPlainText.js.map +1 -0
- package/dist/cjs/editor/renderSafeNodes.js +35 -0
- package/dist/cjs/editor/renderSafeNodes.js.map +1 -0
- package/dist/cjs/editor/safeUrls.js +44 -0
- package/dist/cjs/editor/safeUrls.js.map +1 -0
- package/dist/cjs/editor/sanitizeHtml.js +140 -0
- package/dist/cjs/editor/sanitizeHtml.js.map +1 -0
- package/dist/cjs/editor/serializeSafeNodes.js +33 -0
- package/dist/cjs/editor/serializeSafeNodes.js.map +1 -0
- package/dist/cjs/editor/toolbarLayout.js +35 -0
- package/dist/cjs/editor/toolbarLayout.js.map +1 -0
- package/dist/cjs/editor/useQuillEditor.js +137 -0
- package/dist/cjs/editor/useQuillEditor.js.map +1 -0
- package/dist/cjs/file/FileUploadContext.js +13 -0
- package/dist/cjs/file/FileUploadContext.js.map +1 -0
- package/dist/cjs/file/FileUploadHandlerProvider.js +25 -0
- package/dist/cjs/file/FileUploadHandlerProvider.js.map +1 -0
- package/dist/cjs/file/FileUploadMode.js +13 -0
- package/dist/cjs/file/FileUploadMode.js.map +1 -0
- package/dist/cjs/file/PrimeFileUpload.js +161 -0
- package/dist/cjs/file/PrimeFileUpload.js.map +1 -0
- package/dist/cjs/file/postFiles.js +31 -0
- package/dist/cjs/file/postFiles.js.map +1 -0
- package/dist/cjs/file/resolveUploadTarget.js +68 -0
- package/dist/cjs/file/resolveUploadTarget.js.map +1 -0
- package/dist/cjs/file/useUploadSession.js +62 -0
- package/dist/cjs/file/useUploadSession.js.map +1 -0
- package/dist/cjs/index.js +32 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/legacyEnumerationChoices.js +39 -0
- package/dist/cjs/legacyEnumerationChoices.js.map +1 -0
- package/dist/cjs/loadQuill/index.js +22 -0
- package/dist/cjs/loadQuill/index.js.map +1 -0
- package/dist/cjs/primeReactComponents.js +10 -0
- package/dist/cjs/primeReactComponents.js.map +1 -1
- package/dist/cjs/primeReactDescriptors.js +243 -0
- package/dist/cjs/primeReactDescriptors.js.map +1 -1
- package/dist/cjs/primeReactPackage.js +4 -0
- package/dist/cjs/primeReactPackage.js.map +1 -1
- package/dist/cjs/resolveEnumeration.js +55 -0
- package/dist/cjs/resolveEnumeration.js.map +1 -0
- package/dist/cjs/treeNodes.js +42 -7
- package/dist/cjs/treeNodes.js.map +1 -1
- package/dist/cjs/useResettingState.js +47 -0
- package/dist/cjs/useResettingState.js.map +1 -0
- package/dist/esm/EnumerationResolution.d.ts +9 -0
- package/dist/esm/EnumerationResolution.d.ts.map +1 -0
- package/dist/esm/EnumerationResolution.js +2 -0
- package/dist/esm/EnumerationResolution.js.map +1 -0
- package/dist/esm/data/DataGallery.stories.d.ts.map +1 -1
- package/dist/esm/data/DataGallery.stories.js +6 -0
- package/dist/esm/data/DataGallery.stories.js.map +1 -1
- package/dist/esm/data/DataScrollerRow.d.ts +4 -0
- package/dist/esm/data/DataScrollerRow.d.ts.map +1 -0
- package/dist/esm/data/DataScrollerRow.js +48 -0
- package/dist/esm/data/DataScrollerRow.js.map +1 -0
- package/dist/esm/data/PrimeDataScroller.d.ts +3 -0
- package/dist/esm/data/PrimeDataScroller.d.ts.map +1 -0
- package/dist/esm/data/PrimeDataScroller.js +118 -0
- package/dist/esm/data/PrimeDataScroller.js.map +1 -0
- package/dist/esm/data/PrimeTreeTable.d.ts +3 -0
- package/dist/esm/data/PrimeTreeTable.d.ts.map +1 -0
- package/dist/esm/data/PrimeTreeTable.js +215 -0
- package/dist/esm/data/PrimeTreeTable.js.map +1 -0
- package/dist/esm/data/TreeTableCheckState.d.ts +6 -0
- package/dist/esm/data/TreeTableCheckState.d.ts.map +1 -0
- package/dist/esm/data/TreeTableCheckState.js +11 -0
- package/dist/esm/data/TreeTableCheckState.js.map +1 -0
- package/dist/esm/data/TreeTableRow.d.ts +10 -0
- package/dist/esm/data/TreeTableRow.d.ts.map +1 -0
- package/dist/esm/data/TreeTableRow.js +2 -0
- package/dist/esm/data/TreeTableRow.js.map +1 -0
- package/dist/esm/data/TreeTableRowView.d.ts +21 -0
- package/dist/esm/data/TreeTableRowView.d.ts.map +1 -0
- package/dist/esm/data/TreeTableRowView.js +74 -0
- package/dist/esm/data/TreeTableRowView.js.map +1 -0
- package/dist/esm/data/TreeTableSelectionMode.d.ts +7 -0
- package/dist/esm/data/TreeTableSelectionMode.d.ts.map +1 -0
- package/dist/esm/data/TreeTableSelectionMode.js +12 -0
- package/dist/esm/data/TreeTableSelectionMode.js.map +1 -0
- package/dist/esm/data/checkboxSelection.d.ts +9 -0
- package/dist/esm/data/checkboxSelection.d.ts.map +1 -0
- package/dist/esm/data/checkboxSelection.js +85 -0
- package/dist/esm/data/checkboxSelection.js.map +1 -0
- package/dist/esm/data/index.d.ts +4 -0
- package/dist/esm/data/index.d.ts.map +1 -1
- package/dist/esm/data/index.js +4 -0
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/data/isSerializedLegacyElement.d.ts +3 -0
- package/dist/esm/data/isSerializedLegacyElement.d.ts.map +1 -0
- package/dist/esm/data/isSerializedLegacyElement.js +27 -0
- package/dist/esm/data/isSerializedLegacyElement.js.map +1 -0
- package/dist/esm/data/readableValue.d.ts +2 -0
- package/dist/esm/data/readableValue.d.ts.map +1 -0
- package/dist/esm/data/readableValue.js +29 -0
- package/dist/esm/data/readableValue.js.map +1 -0
- package/dist/esm/data/selectionKeysOf.d.ts +2 -0
- package/dist/esm/data/selectionKeysOf.d.ts.map +1 -0
- package/dist/esm/data/selectionKeysOf.js +38 -0
- package/dist/esm/data/selectionKeysOf.js.map +1 -0
- package/dist/esm/data/treeTableRows.d.ts +6 -0
- package/dist/esm/data/treeTableRows.d.ts.map +1 -0
- package/dist/esm/data/treeTableRows.js +38 -0
- package/dist/esm/data/treeTableRows.js.map +1 -0
- package/dist/esm/data/useIncrementalLoad.d.ts +6 -0
- package/dist/esm/data/useIncrementalLoad.d.ts.map +1 -0
- package/dist/esm/data/useIncrementalLoad.js +44 -0
- package/dist/esm/data/useIncrementalLoad.js.map +1 -0
- package/dist/esm/data/useLoadOnReach.d.ts +3 -0
- package/dist/esm/data/useLoadOnReach.d.ts.map +1 -0
- package/dist/esm/data/useLoadOnReach.js +40 -0
- package/dist/esm/data/useLoadOnReach.js.map +1 -0
- package/dist/esm/data/useTreeGridNavigation.d.ts +17 -0
- package/dist/esm/data/useTreeGridNavigation.d.ts.map +1 -0
- package/dist/esm/data/useTreeGridNavigation.js +73 -0
- package/dist/esm/data/useTreeGridNavigation.js.map +1 -0
- package/dist/esm/editor/PrimeEditor.d.ts +3 -0
- package/dist/esm/editor/PrimeEditor.d.ts.map +1 -0
- package/dist/esm/editor/PrimeEditor.js +94 -0
- package/dist/esm/editor/PrimeEditor.js.map +1 -0
- package/dist/esm/editor/QuillConstructor.d.ts +4 -0
- package/dist/esm/editor/QuillConstructor.d.ts.map +1 -0
- package/dist/esm/editor/QuillConstructor.js +2 -0
- package/dist/esm/editor/QuillConstructor.js.map +1 -0
- package/dist/esm/editor/QuillEditorOptions.d.ts +13 -0
- package/dist/esm/editor/QuillEditorOptions.d.ts.map +1 -0
- package/dist/esm/editor/QuillEditorOptions.js +2 -0
- package/dist/esm/editor/QuillEditorOptions.js.map +1 -0
- package/dist/esm/editor/QuillEditorState.d.ts +6 -0
- package/dist/esm/editor/QuillEditorState.d.ts.map +1 -0
- package/dist/esm/editor/QuillEditorState.js +2 -0
- package/dist/esm/editor/QuillEditorState.js.map +1 -0
- package/dist/esm/editor/QuillInstance.d.ts +16 -0
- package/dist/esm/editor/QuillInstance.d.ts.map +1 -0
- package/dist/esm/editor/QuillInstance.js +2 -0
- package/dist/esm/editor/QuillInstance.js.map +1 -0
- package/dist/esm/editor/QuillLoader.d.ts +5 -0
- package/dist/esm/editor/QuillLoader.d.ts.map +1 -0
- package/dist/esm/editor/QuillLoader.js +2 -0
- package/dist/esm/editor/QuillLoader.js.map +1 -0
- package/dist/esm/editor/QuillLoaderContext.d.ts +3 -0
- package/dist/esm/editor/QuillLoaderContext.d.ts.map +1 -0
- package/dist/esm/editor/QuillLoaderContext.js +8 -0
- package/dist/esm/editor/QuillLoaderContext.js.map +1 -0
- package/dist/esm/editor/QuillLoaderProvider.d.ts +8 -0
- package/dist/esm/editor/QuillLoaderProvider.d.ts.map +1 -0
- package/dist/esm/editor/QuillLoaderProvider.js +19 -0
- package/dist/esm/editor/QuillLoaderProvider.js.map +1 -0
- package/dist/esm/editor/QuillOptions.d.ts +9 -0
- package/dist/esm/editor/QuillOptions.d.ts.map +1 -0
- package/dist/esm/editor/QuillOptions.js +2 -0
- package/dist/esm/editor/QuillOptions.js.map +1 -0
- package/dist/esm/editor/SafeElement.d.ts +7 -0
- package/dist/esm/editor/SafeElement.d.ts.map +1 -0
- package/dist/esm/editor/SafeElement.js +2 -0
- package/dist/esm/editor/SafeElement.js.map +1 -0
- package/dist/esm/editor/SafeNode.d.ts +3 -0
- package/dist/esm/editor/SafeNode.d.ts.map +1 -0
- package/dist/esm/editor/SafeNode.js +2 -0
- package/dist/esm/editor/SafeNode.js.map +1 -0
- package/dist/esm/editor/applyHtml.d.ts +3 -0
- package/dist/esm/editor/applyHtml.d.ts.map +1 -0
- package/dist/esm/editor/applyHtml.js +23 -0
- package/dist/esm/editor/applyHtml.js.map +1 -0
- package/dist/esm/editor/characterCount.d.ts +3 -0
- package/dist/esm/editor/characterCount.d.ts.map +1 -0
- package/dist/esm/editor/characterCount.js +8 -0
- package/dist/esm/editor/characterCount.js.map +1 -0
- package/dist/esm/editor/htmlToPlainText.d.ts +2 -0
- package/dist/esm/editor/htmlToPlainText.d.ts.map +1 -0
- package/dist/esm/editor/htmlToPlainText.js +99 -0
- package/dist/esm/editor/htmlToPlainText.js.map +1 -0
- package/dist/esm/editor/index.d.ts +14 -0
- package/dist/esm/editor/index.d.ts.map +1 -0
- package/dist/esm/editor/index.js +14 -0
- package/dist/esm/editor/index.js.map +1 -0
- package/dist/esm/editor/renderSafeNodes.d.ts +4 -0
- package/dist/esm/editor/renderSafeNodes.d.ts.map +1 -0
- package/dist/esm/editor/renderSafeNodes.js +33 -0
- package/dist/esm/editor/renderSafeNodes.js.map +1 -0
- package/dist/esm/editor/safeUrls.d.ts +3 -0
- package/dist/esm/editor/safeUrls.d.ts.map +1 -0
- package/dist/esm/editor/safeUrls.js +41 -0
- package/dist/esm/editor/safeUrls.js.map +1 -0
- package/dist/esm/editor/sanitizeHtml.d.ts +3 -0
- package/dist/esm/editor/sanitizeHtml.d.ts.map +1 -0
- package/dist/esm/editor/sanitizeHtml.js +138 -0
- package/dist/esm/editor/sanitizeHtml.js.map +1 -0
- package/dist/esm/editor/serializeSafeNodes.d.ts +3 -0
- package/dist/esm/editor/serializeSafeNodes.d.ts.map +1 -0
- package/dist/esm/editor/serializeSafeNodes.js +31 -0
- package/dist/esm/editor/serializeSafeNodes.js.map +1 -0
- package/dist/esm/editor/toolbarLayout.d.ts +2 -0
- package/dist/esm/editor/toolbarLayout.d.ts.map +1 -0
- package/dist/esm/editor/toolbarLayout.js +33 -0
- package/dist/esm/editor/toolbarLayout.js.map +1 -0
- package/dist/esm/editor/useQuillEditor.d.ts +4 -0
- package/dist/esm/editor/useQuillEditor.d.ts.map +1 -0
- package/dist/esm/editor/useQuillEditor.js +135 -0
- package/dist/esm/editor/useQuillEditor.js.map +1 -0
- package/dist/esm/file/FileGallery.stories.d.ts +19 -0
- package/dist/esm/file/FileGallery.stories.d.ts.map +1 -0
- package/dist/esm/file/FileGallery.stories.js +23 -0
- package/dist/esm/file/FileGallery.stories.js.map +1 -0
- package/dist/esm/file/FileUploadContext.d.ts +7 -0
- package/dist/esm/file/FileUploadContext.d.ts.map +1 -0
- package/dist/esm/file/FileUploadContext.js +11 -0
- package/dist/esm/file/FileUploadContext.js.map +1 -0
- package/dist/esm/file/FileUploadHandler.d.ts +4 -0
- package/dist/esm/file/FileUploadHandler.d.ts.map +1 -0
- package/dist/esm/file/FileUploadHandler.js +2 -0
- package/dist/esm/file/FileUploadHandler.js.map +1 -0
- package/dist/esm/file/FileUploadHandlerProvider.d.ts +9 -0
- package/dist/esm/file/FileUploadHandlerProvider.d.ts.map +1 -0
- package/dist/esm/file/FileUploadHandlerProvider.js +23 -0
- package/dist/esm/file/FileUploadHandlerProvider.js.map +1 -0
- package/dist/esm/file/FileUploadMode.d.ts +6 -0
- package/dist/esm/file/FileUploadMode.d.ts.map +1 -0
- package/dist/esm/file/FileUploadMode.js +11 -0
- package/dist/esm/file/FileUploadMode.js.map +1 -0
- package/dist/esm/file/PrimeFileUpload.d.ts +3 -0
- package/dist/esm/file/PrimeFileUpload.d.ts.map +1 -0
- package/dist/esm/file/PrimeFileUpload.js +159 -0
- package/dist/esm/file/PrimeFileUpload.js.map +1 -0
- package/dist/esm/file/UploadStatus.d.ts +5 -0
- package/dist/esm/file/UploadStatus.d.ts.map +1 -0
- package/dist/esm/file/UploadStatus.js +2 -0
- package/dist/esm/file/UploadStatus.js.map +1 -0
- package/dist/esm/file/UploadTarget.d.ts +8 -0
- package/dist/esm/file/UploadTarget.d.ts.map +1 -0
- package/dist/esm/file/UploadTarget.js +2 -0
- package/dist/esm/file/UploadTarget.js.map +1 -0
- package/dist/esm/file/index.d.ts +7 -0
- package/dist/esm/file/index.d.ts.map +1 -0
- package/dist/esm/file/index.js +7 -0
- package/dist/esm/file/index.js.map +1 -0
- package/dist/esm/file/postFiles.d.ts +2 -0
- package/dist/esm/file/postFiles.d.ts.map +1 -0
- package/dist/esm/file/postFiles.js +29 -0
- package/dist/esm/file/postFiles.js.map +1 -0
- package/dist/esm/file/resolveUploadTarget.d.ts +3 -0
- package/dist/esm/file/resolveUploadTarget.d.ts.map +1 -0
- package/dist/esm/file/resolveUploadTarget.js +66 -0
- package/dist/esm/file/resolveUploadTarget.js.map +1 -0
- package/dist/esm/file/useUploadSession.d.ts +10 -0
- package/dist/esm/file/useUploadSession.d.ts.map +1 -0
- package/dist/esm/file/useUploadSession.js +60 -0
- package/dist/esm/file/useUploadSession.js.map +1 -0
- package/dist/esm/form/FormGallery.stories.d.ts.map +1 -1
- package/dist/esm/form/FormGallery.stories.js +1 -0
- package/dist/esm/form/FormGallery.stories.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +16 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/legacyEnumerationChoices.d.ts +3 -0
- package/dist/esm/legacyEnumerationChoices.d.ts.map +1 -0
- package/dist/esm/legacyEnumerationChoices.js +37 -0
- package/dist/esm/legacyEnumerationChoices.js.map +1 -0
- package/dist/esm/loadQuill/index.d.ts +3 -0
- package/dist/esm/loadQuill/index.d.ts.map +1 -0
- package/dist/esm/loadQuill/index.js +20 -0
- package/dist/esm/loadQuill/index.js.map +1 -0
- package/dist/esm/primeReactComponents.d.ts.map +1 -1
- package/dist/esm/primeReactComponents.js +10 -0
- package/dist/esm/primeReactComponents.js.map +1 -1
- package/dist/esm/primeReactDescriptors.d.ts.map +1 -1
- package/dist/esm/primeReactDescriptors.js +243 -0
- package/dist/esm/primeReactDescriptors.js.map +1 -1
- package/dist/esm/primeReactPackage.js +4 -0
- package/dist/esm/primeReactPackage.js.map +1 -1
- package/dist/esm/resolveEnumeration.d.ts +3 -0
- package/dist/esm/resolveEnumeration.d.ts.map +1 -0
- package/dist/esm/resolveEnumeration.js +53 -0
- package/dist/esm/resolveEnumeration.js.map +1 -0
- package/dist/esm/treeNodes.d.ts +2 -1
- package/dist/esm/treeNodes.d.ts.map +1 -1
- package/dist/esm/treeNodes.js +42 -8
- package/dist/esm/treeNodes.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/useResettingState.d.ts +3 -0
- package/dist/esm/useResettingState.d.ts.map +1 -0
- package/dist/esm/useResettingState.js +45 -0
- package/dist/esm/useResettingState.js.map +1 -0
- package/editor/PrimeEditor.tsx +83 -0
- package/editor/QuillConstructor.ts +8 -0
- package/editor/QuillEditorOptions.ts +29 -0
- package/editor/QuillEditorState.ts +14 -0
- package/editor/QuillInstance.ts +27 -0
- package/editor/QuillLoader.ts +13 -0
- package/editor/QuillLoaderContext.ts +8 -0
- package/editor/QuillLoaderProvider.tsx +24 -0
- package/editor/QuillOptions.ts +10 -0
- package/editor/SafeElement.ts +15 -0
- package/editor/SafeNode.ts +7 -0
- package/editor/applyHtml.ts +21 -0
- package/editor/characterCount.ts +9 -0
- package/editor/htmlToPlainText.ts +74 -0
- package/editor/index.ts +16 -0
- package/editor/renderSafeNodes.tsx +26 -0
- package/editor/safeUrls.ts +43 -0
- package/editor/sanitizeHtml.ts +94 -0
- package/editor/serializeSafeNodes.ts +26 -0
- package/editor/toolbarLayout.ts +10 -0
- package/editor/useQuillEditor.ts +114 -0
- package/file/FileGallery.stories.tsx +32 -0
- package/file/FileUploadContext.ts +17 -0
- package/file/FileUploadHandler.ts +15 -0
- package/file/FileUploadHandlerProvider.tsx +33 -0
- package/file/FileUploadMode.ts +9 -0
- package/file/PrimeFileUpload.tsx +129 -0
- package/file/UploadStatus.ts +9 -0
- package/file/UploadTarget.ts +12 -0
- package/file/index.ts +9 -0
- package/file/postFiles.ts +23 -0
- package/file/resolveUploadTarget.ts +61 -0
- package/file/useUploadSession.ts +67 -0
- package/form/FormGallery.stories.tsx +1 -0
- package/index.ts +2 -0
- package/legacyEnumerationChoices.ts +35 -0
- package/loadQuill/index.ts +21 -0
- package/package.json +17 -7
- package/primeReactComponents.ts +8 -0
- package/primeReactDescriptors.ts +63 -0
- package/primeReactPackage.ts +2 -2
- package/primeReactTheme.css +69 -0
- package/resolveEnumeration.ts +50 -0
- package/treeNodes.ts +44 -6
- package/useResettingState.ts +34 -0
package/.storybook/preview.tsx
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import type { Decorator, Preview } from '@storybook/react';
|
|
5
5
|
import { PrimeReactProvider } from '@primereact/core';
|
|
6
|
+
import { QuillLoaderProvider } from '../editor';
|
|
7
|
+
import { loadQuill } from '../loadQuill';
|
|
6
8
|
import { primeReactTheme, usePrimeReactTheme } from '../theme';
|
|
7
9
|
import '../primeReactTheme.css';
|
|
8
10
|
import './preview.css';
|
|
@@ -12,6 +14,9 @@ import './preview.css';
|
|
|
12
14
|
// resolve to. Only the icon font is still a stylesheet.
|
|
13
15
|
import 'primeicons/primeicons.css';
|
|
14
16
|
|
|
17
|
+
// A host that wants editable rich text installs Quill and its stylesheet, and provides the loader.
|
|
18
|
+
import 'quill/dist/quill.snow.css';
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Puts every story inside a `PrimeReactProvider` carrying a real Scene theme.
|
|
17
22
|
*
|
|
@@ -29,7 +34,9 @@ const withTheme: Decorator = (Story) => {
|
|
|
29
34
|
|
|
30
35
|
return (
|
|
31
36
|
<PrimeReactProvider value={configuration}>
|
|
32
|
-
<
|
|
37
|
+
<QuillLoaderProvider loader={loadQuill}>
|
|
38
|
+
<Story />
|
|
39
|
+
</QuillLoaderProvider>
|
|
33
40
|
</PrimeReactProvider>
|
|
34
41
|
);
|
|
35
42
|
};
|
|
@@ -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
|
+
/** The outcome of resolving an authored enumeration value: the member, or the reason none could be chosen. */
|
|
5
|
+
export type EnumerationResolution<TMember extends string> =
|
|
6
|
+
| { isValid: true; value: TMember }
|
|
7
|
+
| { isValid: false; value: unknown; message: string };
|
package/README.md
CHANGED
|
@@ -109,16 +109,9 @@ twenty-fifth theme cannot be added without it. The full attribution table is in
|
|
|
109
109
|
components - through the semantic token layer `SceneThemeProvider` applies to the whole subtree, though
|
|
110
110
|
not through the `--p-*` properties, which only PrimeReact's own components read.
|
|
111
111
|
|
|
112
|
-
##
|
|
112
|
+
## Optional peers
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
| --- | --- |
|
|
116
|
-
| `editor` | `Editor` wraps Quill and needs `quill` installed. Same reasoning — a rich-text editor is a product decision, not a mapping. |
|
|
117
|
-
|
|
118
|
-
The editor is a deliberate omission, not an oversight. A profile needing it should activate a package
|
|
119
|
-
that owns its dependency.
|
|
120
|
-
|
|
121
|
-
## Chart.js peer dependency
|
|
114
|
+
### Chart.js peer dependency
|
|
122
115
|
|
|
123
116
|
PrimeReact 11 removed its former Chart.js wrapper, so the Scene `chart` adapter mounts a Chart.js canvas
|
|
124
117
|
itself. `chart.js@^4.5.1` is an optional peer dependency of `@cratis/scene.primereact`, optional only in that
|
|
@@ -134,6 +127,23 @@ the type, data or options change in content, not when a document edit hands it e
|
|
|
134
127
|
rewriting the stored element. Any other type is reported instead of drawn as a bar chart. A chart without data
|
|
135
128
|
points shows its `emptyLabel`.
|
|
136
129
|
|
|
130
|
+
### Quill peer dependency
|
|
131
|
+
|
|
132
|
+
`quill` is not imported by the package root, so a host that renders no rich text needs neither `quill` nor its
|
|
133
|
+
stylesheet to build. A host that wants editable rich text installs `quill@^2.0.3` and opts in explicitly:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import 'quill/dist/quill.snow.css';
|
|
137
|
+
import { QuillLoaderProvider } from '@cratis/scene.primereact';
|
|
138
|
+
import { loadQuill } from '@cratis/scene.primereact/quill';
|
|
139
|
+
|
|
140
|
+
<QuillLoaderProvider loader={loadQuill}>{screen}</QuillLoaderProvider>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`@cratis/scene.primereact/quill` (sources in `loadQuill/`) is the only module that names `quill`. Without a
|
|
144
|
+
loader the editor shows its content read-only and says so. Authored HTML always passes an allowlist first; see
|
|
145
|
+
the [component reference](../../../Documentation/primereact-package/component-reference.md#rich-text-and-quill).
|
|
146
|
+
|
|
137
147
|
## Browser specifications
|
|
138
148
|
|
|
139
149
|
Most specifications run under jsdom with `yarn test`. Painted pixels, keyboard behavior and computed theme
|
|
@@ -162,9 +172,9 @@ The short version for a contributor:
|
|
|
162
172
|
compiles cleanly and silently swaps an overlay for a static shell.
|
|
163
173
|
- **v11 ships zero CSS.** There is no `primereact/resources` directory. A look comes from a preset handed
|
|
164
174
|
to `PrimeReactProvider`; `usePrimeReactTheme` returns that configuration for a Scene theme.
|
|
165
|
-
- **
|
|
166
|
-
2 are expressed over `@primereact/headless` hooks, and
|
|
167
|
-
(`cascadeSelect`, `inputMask`, `
|
|
175
|
+
- **16 abstract names are now backed by Cratis-owned components** built here because v11 removed theirs,
|
|
176
|
+
2 are expressed over `@primereact/headless` hooks, and 3 were dropped from the manifest
|
|
177
|
+
(`cascadeSelect`, `inputMask`, `virtualScroller`).
|
|
168
178
|
- **`MenuItem` and `TreeNode` are Scene's types now** - `primereact/menuitem` and `primereact/treenode`
|
|
169
179
|
were removed. Import them from this package, never from `primereact/*`.
|
|
170
180
|
|
|
@@ -37,7 +37,13 @@ export const Gallery: Story = {
|
|
|
37
37
|
),
|
|
38
38
|
sceneComponent('dataTable', 'dataTable', { rows: [{ id: 1, title: 'Inferred columns' }] }),
|
|
39
39
|
sceneComponent('dataView', 'dataView', { rows: [{ title: 'Kickoff', description: 'Agree the scope' }] }),
|
|
40
|
+
sceneComponent('dataScroller', 'dataScroller', { items: ['First', 'Second', 'Third'], rows: 2, inline: true, scrollHeight: 96 }),
|
|
40
41
|
sceneComponent('tree', 'tree', { nodes: [{ label: 'Documents', children: ['Work', 'Home'] }] }),
|
|
42
|
+
sceneComponent('treeTable', 'treeTable', {
|
|
43
|
+
columns: [{ field: 'name', header: 'Name' }, { field: 'type', header: 'Type' }],
|
|
44
|
+
items: [{ label: 'Documents', data: { name: 'Documents', type: 'Folder' }, children: [{ label: 'Plan', data: { name: 'Plan', type: 'File' } }] }],
|
|
45
|
+
selectionMode: 'checkbox', paginator: true, rows: 1,
|
|
46
|
+
}),
|
|
41
47
|
sceneComponent('timeline', 'timeline', {
|
|
42
48
|
events: [{ title: 'Ordered', description: 'Order placed', date: '09:00' }, { title: 'Shipped', description: 'Left the depot', date: '14:20' }],
|
|
43
49
|
}),
|
|
@@ -0,0 +1,35 @@
|
|
|
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 { readableValue } from './readableValue';
|
|
5
|
+
|
|
6
|
+
/** The fields that, when present as text, name a record. */
|
|
7
|
+
const titleFields = ['label', 'title', 'name', 'text', 'header'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One authored data row, shown readably.
|
|
11
|
+
*
|
|
12
|
+
* A scalar is its text. A record is titled by its first text field among `label`, `title`, `name`, `text` and
|
|
13
|
+
* `header`, and every other field follows as a name and a readable value. No row is ever dumped as JSON.
|
|
14
|
+
*/
|
|
15
|
+
export function DataScrollerRow({ value }: { value: unknown }) {
|
|
16
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return <>{readableValue(value)}</>;
|
|
17
|
+
|
|
18
|
+
const record = value as Record<string, unknown>;
|
|
19
|
+
const titleField = titleFields.find(field => typeof record[field] === 'string' && record[field] !== '');
|
|
20
|
+
const details = Object.entries(record)
|
|
21
|
+
.filter(([name]) => name !== titleField)
|
|
22
|
+
.map(([name, entry]) => [name, readableValue(entry)] as const)
|
|
23
|
+
.filter(([, text]) => text !== '');
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
{titleField !== undefined && <strong>{record[titleField] as string}</strong>}
|
|
28
|
+
{details.length > 0 && (
|
|
29
|
+
<dl>
|
|
30
|
+
{details.map(([name, text]) => <div key={name}><dt>{name}</dt><dd>{text}</dd></div>)}
|
|
31
|
+
</dl>
|
|
32
|
+
)}
|
|
33
|
+
</>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
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, useEffect, useMemo, useRef } from 'react';
|
|
5
|
+
import { RegisteredComponentProps } from '@cratis/scene.react';
|
|
6
|
+
import { arrayProperty, booleanProperty, numberProperty, stringProperty } from '../properties';
|
|
7
|
+
import { useStructuralValue } from '../useStructuralValue';
|
|
8
|
+
import { DataScrollerRow } from './DataScrollerRow';
|
|
9
|
+
import { isSerializedLegacyElement, legacyElementMessage } from './isSerializedLegacyElement';
|
|
10
|
+
import { useIncrementalLoad } from './useIncrementalLoad';
|
|
11
|
+
import { useLoadOnReach } from './useLoadOnReach';
|
|
12
|
+
|
|
13
|
+
/** How tall an inline scroller is when no `scrollHeight` was authored: without a height it would never overflow. */
|
|
14
|
+
const defaultScrollHeight = 320;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The `PrimeReact:dataScroller` component, a list that loads a chunk at a time as its end is reached.
|
|
18
|
+
*
|
|
19
|
+
* The entries are read as data: strings, numbers and records (see {@link DataScrollerRow}) from `items`, then
|
|
20
|
+
* any child elements authored in the `items` (or `content`) slot, which render as themselves. Serialized
|
|
21
|
+
* legacy UI elements in `items` are refused with a visible message, not shown as JSON.
|
|
22
|
+
*
|
|
23
|
+
* - `inline: true` scrolls inside the control, `scrollHeight` pixels tall (320 when omitted). `inline: false`
|
|
24
|
+
* loads as the page scrolls the end of the list into view.
|
|
25
|
+
* - The "Load more" button remains for keyboard and assistive technology; focus moves to the first entry it
|
|
26
|
+
* loads, and the number shown is announced.
|
|
27
|
+
* - A disabled element loads nothing on scroll and its button is disabled.
|
|
28
|
+
* - The loaded count restarts when `rows` or the content of `items` changes.
|
|
29
|
+
*/
|
|
30
|
+
export function PrimeDataScroller({ element, slots, interactions }: RegisteredComponentProps) {
|
|
31
|
+
const authored = useStructuralValue(arrayProperty(element, 'items'));
|
|
32
|
+
const data = useMemo(() => authored.filter(entry => !isSerializedLegacyElement(entry) && entry !== null && entry !== undefined), [authored]);
|
|
33
|
+
const refusal = legacyElementMessage(authored, 'items', "Author them as child elements in the 'items' slot, or as data.");
|
|
34
|
+
const slotted = slots.items ?? slots.content ?? [];
|
|
35
|
+
const rows = useMemo<ReactNode[]>(() => [...data.map((value, index) => <DataScrollerRow key={index} value={value} />), ...slotted], [data, slotted]);
|
|
36
|
+
const chunk = Math.max(Math.floor(numberProperty(element, 'rows', 10)), 1);
|
|
37
|
+
const inline = booleanProperty(element, 'inline', false);
|
|
38
|
+
const scrollHeight = numberProperty(element, 'scrollHeight', defaultScrollHeight);
|
|
39
|
+
const enabled = element.isEnabled;
|
|
40
|
+
const { loaded, loadMore } = useIncrementalLoad(rows.length, chunk, data);
|
|
41
|
+
const canLoad = loaded < rows.length;
|
|
42
|
+
const scroller = useRef<HTMLDivElement>(null);
|
|
43
|
+
const sentinel = useRef<HTMLDivElement>(null);
|
|
44
|
+
const entries = useRef<(HTMLLIElement | null)[]>([]);
|
|
45
|
+
const focusIndex = useRef<number | undefined>(undefined);
|
|
46
|
+
|
|
47
|
+
useLoadOnReach(sentinel, inline ? scroller : null, enabled && canLoad, loaded, loadMore);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (focusIndex.current === undefined) return;
|
|
51
|
+
entries.current[focusIndex.current]?.focus();
|
|
52
|
+
focusIndex.current = undefined;
|
|
53
|
+
}, [loaded]);
|
|
54
|
+
|
|
55
|
+
const loadByButton = () => {
|
|
56
|
+
focusIndex.current = loaded;
|
|
57
|
+
loadMore();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
data-scene-id={element.id}
|
|
63
|
+
data-scene-component='dataScroller'
|
|
64
|
+
aria-disabled={!enabled}
|
|
65
|
+
onClick={interactions?.onClick}
|
|
66
|
+
onDoubleClick={interactions?.onDoubleClick}>
|
|
67
|
+
<div
|
|
68
|
+
ref={scroller}
|
|
69
|
+
role='region'
|
|
70
|
+
tabIndex={inline ? 0 : undefined}
|
|
71
|
+
aria-label={stringProperty(element, 'ariaLabel', 'Scrollable data')}
|
|
72
|
+
style={inline ? { maxHeight: `${scrollHeight}px`, overflowY: 'auto' } : undefined}>
|
|
73
|
+
<ol>
|
|
74
|
+
{rows.slice(0, loaded).map((row, index) => <li key={index} tabIndex={-1} ref={node => { entries.current[index] = node; }}>{row}</li>)}
|
|
75
|
+
</ol>
|
|
76
|
+
{canLoad && <div ref={sentinel} aria-hidden='true' />}
|
|
77
|
+
</div>
|
|
78
|
+
{rows.length === 0 && <p role='status'>{stringProperty(element, 'emptyLabel', 'No items to show')}</p>}
|
|
79
|
+
{refusal !== undefined && <p role='alert' data-scene-state='legacy-elements'>{refusal}</p>}
|
|
80
|
+
{canLoad && <button type='button' disabled={!enabled} onClick={loadByButton}>Load more</button>}
|
|
81
|
+
{rows.length > 0 && <output aria-live='polite'>{`Showing ${loaded} of ${rows.length} items`}</output>}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
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 { useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { RegisteredComponentProps } from '@cratis/scene.react';
|
|
6
|
+
import { arrayProperty, booleanProperty, numberProperty, stringProperty } from '../properties';
|
|
7
|
+
import { resolveEnumeration } from '../resolveEnumeration';
|
|
8
|
+
import { countUnreadableEntries, toTreeNodes } from '../treeNodes';
|
|
9
|
+
import { useResettingState } from '../useResettingState';
|
|
10
|
+
import { useStructuralValue } from '../useStructuralValue';
|
|
11
|
+
import { ColumnDefinition } from './ColumnDefinition';
|
|
12
|
+
import { TreeTableRow } from './TreeTableRow';
|
|
13
|
+
import { TreeTableRowView } from './TreeTableRowView';
|
|
14
|
+
import { TreeTableSelectionMode } from './TreeTableSelectionMode';
|
|
15
|
+
import { columnDefinitions } from './columnDefinitions';
|
|
16
|
+
import { SettledSelection, settleSelection, toggleCheckbox } from './checkboxSelection';
|
|
17
|
+
import { isSerializedLegacyElement, legacyElementMessage } from './isSerializedLegacyElement';
|
|
18
|
+
import { selectionKeysOf } from './selectionKeysOf';
|
|
19
|
+
import { expandedKeysOf, visibleRows } from './treeTableRows';
|
|
20
|
+
import { useTreeGridNavigation } from './useTreeGridNavigation';
|
|
21
|
+
|
|
22
|
+
const labelColumn: ColumnDefinition[] = [{ field: 'label', header: 'Label', sortable: false }];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The `PrimeReact:treeTable` component, an ARIA tree grid over the canonical Scene node and column properties.
|
|
26
|
+
*
|
|
27
|
+
* `items` is an array of nodes - strings, numbers, or records with `key`, `label`, `data`, `children`,
|
|
28
|
+
* `expanded` and `selectable` - and the columns come from nested `column` elements, a `columns` property or the
|
|
29
|
+
* fields of the first row's `data`, in that order (see `columnDefinitions`). Serialized legacy UI elements in
|
|
30
|
+
* `items` or `columns` are refused with a visible message; see the package documentation for how they map to
|
|
31
|
+
* canonical slots.
|
|
32
|
+
*
|
|
33
|
+
* - Selection follows the authored `selection` (keys, numbers, `{ key }` objects or PrimeReact's key map) and
|
|
34
|
+
* starts over when it changes; expansion starts from the authored `expanded` flags the same way. An unrelated
|
|
35
|
+
* property edit leaves what the user did alone.
|
|
36
|
+
* - In checkbox mode a parent checks and unchecks its subtree and shows a partial state when only some of it is
|
|
37
|
+
* checked.
|
|
38
|
+
* - The rows form a `treegrid` with a roving tab index and the usual arrow-key navigation.
|
|
39
|
+
* - Object cell values are shown readably, never as `[object Object]`.
|
|
40
|
+
*/
|
|
41
|
+
export function PrimeTreeTable({ element, interactions }: RegisteredComponentProps) {
|
|
42
|
+
const authoredItems = useStructuralValue(arrayProperty(element, 'items'));
|
|
43
|
+
const items = useMemo(() => authoredItems.filter(entry => !isSerializedLegacyElement(entry)), [authoredItems]);
|
|
44
|
+
const nodes = useMemo(() => toTreeNodes(items), [items]);
|
|
45
|
+
const modeResolution = resolveEnumeration('selectionMode', element.properties.selectionMode, TreeTableSelectionMode, TreeTableSelectionMode.None);
|
|
46
|
+
const mode = modeResolution.isValid ? modeResolution.value : TreeTableSelectionMode.None;
|
|
47
|
+
const enabled = element.isEnabled;
|
|
48
|
+
const pageSize = Math.max(Math.floor(numberProperty(element, 'rows', 10)), 1);
|
|
49
|
+
const paginator = booleanProperty(element, 'paginator', false);
|
|
50
|
+
const [expanded, setExpanded] = useResettingState(expandedKeysOf(nodes), () => new Set(expandedKeysOf(nodes)));
|
|
51
|
+
const [requested, setRequested] = useResettingState(element.properties.selection, () => selectionKeysOf(element.properties.selection));
|
|
52
|
+
const [page, setPage] = useState(0);
|
|
53
|
+
const pageCount = Math.max(Math.ceil(nodes.length / pageSize), 1);
|
|
54
|
+
const currentPage = Math.min(page, pageCount - 1);
|
|
55
|
+
const rows = useMemo(() => visibleRows(paginator ? nodes.slice(currentPage * pageSize, (currentPage + 1) * pageSize) : nodes, expanded), [nodes, paginator, currentPage, pageSize, expanded]);
|
|
56
|
+
const settled = useMemo<SettledSelection>(
|
|
57
|
+
() => mode === TreeTableSelectionMode.Checkbox ? settleSelection(nodes, requested) : { checked: requested, states: new Map() },
|
|
58
|
+
[mode, nodes, requested]
|
|
59
|
+
);
|
|
60
|
+
const columns = useMemo(() => {
|
|
61
|
+
const first = rows.find(row => typeof row.node.data === 'object' && row.node.data !== null && !Array.isArray(row.node.data))?.node.data as Record<string, unknown> | undefined;
|
|
62
|
+
const defined = columnDefinitions(element, first === undefined ? [] : [first]);
|
|
63
|
+
return defined.length > 0 ? defined : labelColumn;
|
|
64
|
+
}, [element, rows]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => setPage(current => Math.min(current, pageCount - 1)), [pageCount]);
|
|
67
|
+
|
|
68
|
+
const toggle = (key: string) => {
|
|
69
|
+
if (enabled) setExpanded(current => {
|
|
70
|
+
const next = new Set(current);
|
|
71
|
+
if (next.has(key)) next.delete(key); else next.add(key);
|
|
72
|
+
return next;
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const select = (row: TreeTableRow) => {
|
|
77
|
+
if (mode === TreeTableSelectionMode.None || !enabled || row.node.selectable === false) return;
|
|
78
|
+
if (mode === TreeTableSelectionMode.Checkbox) {
|
|
79
|
+
setRequested(toggleCheckbox(nodes, settled, row.node));
|
|
80
|
+
} else if (mode === TreeTableSelectionMode.Single) {
|
|
81
|
+
setRequested(new Set([row.key]));
|
|
82
|
+
} else {
|
|
83
|
+
const next = new Set(settled.checked);
|
|
84
|
+
if (next.has(row.key)) next.delete(row.key); else next.add(row.key);
|
|
85
|
+
setRequested(next);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interactions?.onSelect?.();
|
|
89
|
+
interactions?.onChange?.();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const navigation = useTreeGridNavigation({ rows, expanded, enabled, toggle, select });
|
|
93
|
+
const changePage = (target: number) => { if (enabled && target >= 0 && target < pageCount) setPage(target); };
|
|
94
|
+
const refusals = [
|
|
95
|
+
modeResolution.isValid ? undefined : modeResolution.message,
|
|
96
|
+
legacyElementMessage(authoredItems, 'items', 'Provide items as node data: strings, numbers, or records with key, label, data and children.'),
|
|
97
|
+
legacyElementMessage(arrayProperty(element, 'columns'), 'columns', "Author them as child elements in the 'columns' slot, or as column definitions."),
|
|
98
|
+
].filter((message): message is string => message !== undefined);
|
|
99
|
+
const skipped = countUnreadableEntries(items);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div data-scene-id={element.id} data-scene-component='treeTable' aria-disabled={!enabled} onClick={interactions?.onClick} onDoubleClick={interactions?.onDoubleClick}>
|
|
103
|
+
<table
|
|
104
|
+
role='treegrid'
|
|
105
|
+
aria-label={stringProperty(element, 'ariaLabel', 'Hierarchical data')}
|
|
106
|
+
aria-multiselectable={mode === TreeTableSelectionMode.Multiple || mode === TreeTableSelectionMode.Checkbox ? true : undefined}>
|
|
107
|
+
<thead>
|
|
108
|
+
<tr role='row'>{columns.map((column, index) => <th key={index} role='columnheader'>{column.header}</th>)}</tr>
|
|
109
|
+
</thead>
|
|
110
|
+
<tbody>
|
|
111
|
+
{rows.map(row => (
|
|
112
|
+
<TreeTableRowView
|
|
113
|
+
key={row.key}
|
|
114
|
+
row={row}
|
|
115
|
+
columns={columns}
|
|
116
|
+
mode={mode}
|
|
117
|
+
enabled={enabled}
|
|
118
|
+
expanded={expanded.has(row.key)}
|
|
119
|
+
selected={settled.checked.has(row.key)}
|
|
120
|
+
checkState={settled.states.get(row.key)}
|
|
121
|
+
tabStop={navigation.tabStopKey === row.key}
|
|
122
|
+
register={navigation.register}
|
|
123
|
+
onActivate={() => navigation.activate(row.key)}
|
|
124
|
+
onToggle={() => toggle(row.key)}
|
|
125
|
+
onSelect={() => select(row)}
|
|
126
|
+
onKeyDown={event => navigation.onKeyDown(row, event)}
|
|
127
|
+
/>
|
|
128
|
+
))}
|
|
129
|
+
{rows.length === 0 && (
|
|
130
|
+
<tr role='row'><td role='gridcell' colSpan={columns.length}>{stringProperty(element, 'emptyLabel', 'No records found')}</td></tr>
|
|
131
|
+
)}
|
|
132
|
+
</tbody>
|
|
133
|
+
</table>
|
|
134
|
+
{paginator && (
|
|
135
|
+
<nav aria-label='Tree table pages'>
|
|
136
|
+
<button type='button' aria-disabled={!enabled || currentPage === 0} onClick={() => changePage(currentPage - 1)}>Previous</button>
|
|
137
|
+
<span>{currentPage + 1} / {pageCount}</span>
|
|
138
|
+
<button type='button' aria-disabled={!enabled || currentPage + 1 >= pageCount} onClick={() => changePage(currentPage + 1)}>Next</button>
|
|
139
|
+
</nav>
|
|
140
|
+
)}
|
|
141
|
+
{refusals.map(message => <p key={message} role='alert' data-scene-state='refused'>{message}</p>)}
|
|
142
|
+
{skipped > 0 && <p role='status'>{`${skipped} entr${skipped === 1 ? 'y' : 'ies'} of items could not be read as a node and ${skipped === 1 ? 'was' : 'were'} skipped.`}</p>}
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
@@ -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
|
+
/** How much of a tree table row's subtree is checked, in checkbox selection mode. */
|
|
5
|
+
export enum TreeTableCheckState {
|
|
6
|
+
Unchecked = 'unchecked',
|
|
7
|
+
Partial = 'partial',
|
|
8
|
+
Checked = 'checked',
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
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 { TreeNode } from '../TreeNode';
|
|
5
|
+
|
|
6
|
+
/** One visible row of a tree table, with where it sits in the tree. */
|
|
7
|
+
export interface TreeTableRow {
|
|
8
|
+
node: TreeNode;
|
|
9
|
+
key: string;
|
|
10
|
+
|
|
11
|
+
/** Zero for a root row. */
|
|
12
|
+
depth: number;
|
|
13
|
+
|
|
14
|
+
/** The key of the parent row, `undefined` for a root row. */
|
|
15
|
+
parentKey: string | undefined;
|
|
16
|
+
|
|
17
|
+
/** The 1-based position among the siblings, for `aria-posinset`. */
|
|
18
|
+
position: number;
|
|
19
|
+
|
|
20
|
+
/** The number of siblings, for `aria-setsize`. */
|
|
21
|
+
siblings: number;
|
|
22
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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 { ColumnDefinition } from './ColumnDefinition';
|
|
5
|
+
import { TreeTableCheckState } from './TreeTableCheckState';
|
|
6
|
+
import { TreeTableRow } from './TreeTableRow';
|
|
7
|
+
import { TreeTableSelectionMode } from './TreeTableSelectionMode';
|
|
8
|
+
import { nodeLabel } from './treeTableRows';
|
|
9
|
+
import { readableValue } from './readableValue';
|
|
10
|
+
|
|
11
|
+
export interface TreeTableRowViewProps {
|
|
12
|
+
row: TreeTableRow;
|
|
13
|
+
columns: ColumnDefinition[];
|
|
14
|
+
mode: TreeTableSelectionMode;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
expanded: boolean;
|
|
17
|
+
selected: boolean;
|
|
18
|
+
checkState: TreeTableCheckState | undefined;
|
|
19
|
+
tabStop: boolean;
|
|
20
|
+
register(key: string, element: HTMLElement | null): void;
|
|
21
|
+
onActivate(): void;
|
|
22
|
+
onToggle(): void;
|
|
23
|
+
onSelect(): void;
|
|
24
|
+
onKeyDown(event: React.KeyboardEvent<HTMLElement>): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** One `row` of the tree grid: its cells, its expand button and, in checkbox mode, its tri-state checkbox. */
|
|
28
|
+
export function TreeTableRowView({ row, columns, mode, enabled, expanded, selected, checkState, tabStop, register, onActivate, onToggle, onSelect, onKeyDown }: TreeTableRowViewProps) {
|
|
29
|
+
const { node } = row;
|
|
30
|
+
const label = nodeLabel(node);
|
|
31
|
+
const hasChildren = (node.children?.length ?? 0) > 0;
|
|
32
|
+
const selectionOffered = mode !== TreeTableSelectionMode.None;
|
|
33
|
+
const rowSelectable = selectionOffered && node.selectable !== false;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<tr
|
|
37
|
+
ref={element => register(row.key, element)}
|
|
38
|
+
role='row'
|
|
39
|
+
tabIndex={tabStop ? 0 : -1}
|
|
40
|
+
aria-level={row.depth + 1}
|
|
41
|
+
aria-posinset={row.position}
|
|
42
|
+
aria-setsize={row.siblings}
|
|
43
|
+
aria-expanded={hasChildren ? expanded : undefined}
|
|
44
|
+
aria-selected={selectionOffered ? selected : undefined}
|
|
45
|
+
className={node.className}
|
|
46
|
+
onFocus={onActivate}
|
|
47
|
+
onKeyDown={onKeyDown}
|
|
48
|
+
onClick={event => {
|
|
49
|
+
onActivate();
|
|
50
|
+
if (mode === TreeTableSelectionMode.Single || mode === TreeTableSelectionMode.Multiple) {
|
|
51
|
+
if (!(event.target as HTMLElement).closest('button, input')) onSelect();
|
|
52
|
+
}
|
|
53
|
+
}}>
|
|
54
|
+
{columns.map((column, index) => (
|
|
55
|
+
<td key={index} role='gridcell' style={index === 0 ? { paddingInlineStart: `${row.depth * 1.25}rem` } : undefined}>
|
|
56
|
+
{index === 0 && hasChildren && (
|
|
57
|
+
<button type='button' tabIndex={-1} disabled={!enabled} aria-expanded={expanded} aria-label={`${expanded ? 'Collapse' : 'Expand'} ${label}`} onClick={onToggle}>
|
|
58
|
+
{expanded ? 'Collapse' : 'Expand'}
|
|
59
|
+
</button>
|
|
60
|
+
)}
|
|
61
|
+
{index === 0 && mode === TreeTableSelectionMode.Checkbox && checkState !== undefined && (
|
|
62
|
+
<input
|
|
63
|
+
type='checkbox'
|
|
64
|
+
tabIndex={-1}
|
|
65
|
+
ref={input => { if (input !== null) input.indeterminate = checkState === TreeTableCheckState.Partial; }}
|
|
66
|
+
checked={checkState === TreeTableCheckState.Checked}
|
|
67
|
+
disabled={!enabled || !rowSelectable}
|
|
68
|
+
aria-label={`Select ${label}`}
|
|
69
|
+
aria-checked={checkState === TreeTableCheckState.Partial ? 'mixed' : checkState === TreeTableCheckState.Checked}
|
|
70
|
+
onChange={onSelect}
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
{cellText(row, column.field, index === 0)}
|
|
74
|
+
</td>
|
|
75
|
+
))}
|
|
76
|
+
</tr>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function cellText(row: TreeTableRow, field: string, firstColumn: boolean): string {
|
|
81
|
+
const { data } = row.node;
|
|
82
|
+
const record = typeof data === 'object' && data !== null && !Array.isArray(data) ? data as Record<string, unknown> : undefined;
|
|
83
|
+
if (record !== undefined && record[field] !== undefined) return readableValue(record[field]);
|
|
84
|
+
if (!firstColumn) return '';
|
|
85
|
+
return row.node.label ?? (record === undefined ? readableValue(data) : '');
|
|
86
|
+
}
|
|
@@ -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 canonical selection behavior for a hierarchical table. */
|
|
5
|
+
export enum TreeTableSelectionMode {
|
|
6
|
+
None = 'none',
|
|
7
|
+
Single = 'single',
|
|
8
|
+
Multiple = 'multiple',
|
|
9
|
+
Checkbox = 'checkbox',
|
|
10
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { TreeNode } from '../TreeNode';
|
|
5
|
+
import { TreeTableCheckState } from './TreeTableCheckState';
|
|
6
|
+
|
|
7
|
+
/** The outcome of settling a checkbox selection: the keys that are checked, and how each row reads. */
|
|
8
|
+
export interface SettledSelection {
|
|
9
|
+
checked: Set<string>;
|
|
10
|
+
states: Map<string, TreeTableCheckState>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const selectable = (node: TreeNode) => node.selectable !== false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Settles a checkbox selection against the tree, the way a checkbox tree reads:
|
|
17
|
+
* - a checked parent has all of its selectable descendants checked;
|
|
18
|
+
* - a parent is checked when all of its selectable children are, partially checked when some are, and
|
|
19
|
+
* unchecked otherwise;
|
|
20
|
+
* - a node that is not selectable, and has nothing selectable below it, takes no part.
|
|
21
|
+
*
|
|
22
|
+
* Applying this to what the user toggled, or to an authored selection, is what keeps both consistent.
|
|
23
|
+
*
|
|
24
|
+
* @param nodes The root nodes.
|
|
25
|
+
* @param requested The keys asked for.
|
|
26
|
+
*/
|
|
27
|
+
export function settleSelection(nodes: TreeNode[], requested: ReadonlySet<string>): SettledSelection {
|
|
28
|
+
const cascaded = new Set<string>();
|
|
29
|
+
const cascade = (node: TreeNode, inherited: boolean) => {
|
|
30
|
+
const on = inherited || requested.has(node.key ?? '');
|
|
31
|
+
if (on) cascaded.add(node.key ?? '');
|
|
32
|
+
node.children?.forEach(child => cascade(child, on));
|
|
33
|
+
};
|
|
34
|
+
nodes.forEach(node => cascade(node, false));
|
|
35
|
+
|
|
36
|
+
const checked = new Set<string>();
|
|
37
|
+
const states = new Map<string, TreeTableCheckState>();
|
|
38
|
+
const settle = (node: TreeNode): TreeTableCheckState | undefined => {
|
|
39
|
+
const key = node.key ?? '';
|
|
40
|
+
const childStates = (node.children ?? []).map(settle).filter((state): state is TreeTableCheckState => state !== undefined);
|
|
41
|
+
let state: TreeTableCheckState | undefined;
|
|
42
|
+
if (childStates.length === 0) {
|
|
43
|
+
state = selectable(node) ? (cascaded.has(key) ? TreeTableCheckState.Checked : TreeTableCheckState.Unchecked) : undefined;
|
|
44
|
+
} else if (childStates.every(child => child === TreeTableCheckState.Checked)) {
|
|
45
|
+
state = TreeTableCheckState.Checked;
|
|
46
|
+
} else {
|
|
47
|
+
state = childStates.some(child => child !== TreeTableCheckState.Unchecked) ? TreeTableCheckState.Partial : TreeTableCheckState.Unchecked;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (state !== undefined) states.set(key, state);
|
|
51
|
+
if (state === TreeTableCheckState.Checked) checked.add(key);
|
|
52
|
+
return state;
|
|
53
|
+
};
|
|
54
|
+
nodes.forEach(settle);
|
|
55
|
+
|
|
56
|
+
return { checked, states };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Toggles one row's checkbox: a checked row unchecks its whole subtree, any other row checks it.
|
|
61
|
+
*
|
|
62
|
+
* @param nodes The root nodes.
|
|
63
|
+
* @param settled The current settled selection.
|
|
64
|
+
* @param node The row's node.
|
|
65
|
+
* @returns The keys to settle again.
|
|
66
|
+
*/
|
|
67
|
+
export function toggleCheckbox(nodes: TreeNode[], settled: SettledSelection, node: TreeNode): Set<string> {
|
|
68
|
+
const subtree: string[] = [];
|
|
69
|
+
const collect = (current: TreeNode) => {
|
|
70
|
+
subtree.push(current.key ?? '');
|
|
71
|
+
current.children?.forEach(collect);
|
|
72
|
+
};
|
|
73
|
+
collect(node);
|
|
74
|
+
|
|
75
|
+
const next = new Set(settled.checked);
|
|
76
|
+
const checking = settled.states.get(node.key ?? '') !== TreeTableCheckState.Checked;
|
|
77
|
+
subtree.forEach(key => { if (checking) next.add(key); else next.delete(key); });
|
|
78
|
+
|
|
79
|
+
// A checked ancestor would check the unchecked subtree again, so unchecking also unchecks the ancestors.
|
|
80
|
+
if (!checking) (ancestorKeys(nodes, node.key ?? '') ?? []).forEach(key => next.delete(key));
|
|
81
|
+
return settleSelection(nodes, next).checked;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function ancestorKeys(nodes: TreeNode[], key: string, path: string[] = []): string[] | undefined {
|
|
85
|
+
for (const node of nodes) {
|
|
86
|
+
if (node.key === key) return path;
|
|
87
|
+
const found = ancestorKeys(node.children ?? [], key, [...path, node.key ?? '']);
|
|
88
|
+
if (found !== undefined) return found;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
package/data/index.ts
CHANGED
|
@@ -13,3 +13,7 @@ export * from './PrimePaginator';
|
|
|
13
13
|
export * from './PrimeOrderList';
|
|
14
14
|
export * from './PrimePickList';
|
|
15
15
|
export * from './PrimeOrganizationChart';
|
|
16
|
+
export * from './PrimeDataScroller';
|
|
17
|
+
export * from './PrimeTreeTable';
|
|
18
|
+
export * from './TreeTableSelectionMode';
|
|
19
|
+
export * from './readableValue';
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* Whether an entry is a serialized legacy UI element - an object that names its type in `_derivedTypeId`.
|
|
6
|
+
*
|
|
7
|
+
* The original `ItemsControl.Items` and `TreeTable.Columns` held UI elements, not data. Scene does not
|
|
8
|
+
* read those shapes and has no runtime adapter for them: the controls recognize them only to refuse them
|
|
9
|
+
* visibly, instead of listing the element's fields as if they were rows. Converting them is the migration
|
|
10
|
+
* tool's job, which maps each element to a canonical slot (see the package documentation).
|
|
11
|
+
*
|
|
12
|
+
* @param entry An entry of `items` or `columns`.
|
|
13
|
+
*/
|
|
14
|
+
export function isSerializedLegacyElement(entry: unknown): boolean {
|
|
15
|
+
return typeof entry === 'object' && entry !== null && !Array.isArray(entry) && typeof (entry as { _derivedTypeId?: unknown })._derivedTypeId === 'string';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The message for entries that were refused, or `undefined` when there were none.
|
|
20
|
+
*
|
|
21
|
+
* @param entries The authored entries.
|
|
22
|
+
* @param property The property they came from.
|
|
23
|
+
* @param guidance What the author should do instead, finishing the sentence "Author them ..." or similar.
|
|
24
|
+
*/
|
|
25
|
+
export function legacyElementMessage(entries: unknown[], property: string, guidance: string): string | undefined {
|
|
26
|
+
const refused = entries.filter(isSerializedLegacyElement).length;
|
|
27
|
+
return refused === 0
|
|
28
|
+
? undefined
|
|
29
|
+
: `${refused} entr${refused === 1 ? 'y' : 'ies'} of ${property} ${refused === 1 ? 'is a' : 'are'} serialized legacy UI element${refused === 1 ? '' : 's'}, which this control does not read. ${guidance}`;
|
|
30
|
+
}
|