@qoretechnologies/reqraft 0.10.55-pr.124.g43fa138 → 0.10.55
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/.claude/CLAUDE.md +25 -0
- package/.tasks/INDEX.md +1 -0
- package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
- package/README.md +1 -1
- package/design/FORM_ENGINE_COMPACT.md +34 -0
- package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
- package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
- package/design/IDE_INTEGRATION.md +13 -0
- package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
- package/design/SELECT_DIALOG_FLOOR.md +86 -0
- package/design/SMART_EDITOR_UX.md +21 -0
- package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
- package/dist/components/composer/Composer.d.ts +8 -3
- package/dist/components/composer/Composer.d.ts.map +1 -1
- package/dist/components/composer/Composer.js.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
- package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
- package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
- package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/dpqlTags.d.ts +8 -0
- package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlTags.js +5 -1
- package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
- package/dist/components/dpqlEditor/index.d.ts +4 -0
- package/dist/components/dpqlEditor/index.d.ts.map +1 -1
- package/dist/components/dpqlEditor/index.js +5 -1
- package/dist/components/dpqlEditor/index.js.map +1 -1
- package/dist/components/dpqlEditor/types.d.ts +42 -2
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/types.js +0 -4
- package/dist/components/dpqlEditor/types.js.map +1 -1
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
- package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
- package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +414 -128
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +5 -2
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +273 -22
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +669 -192
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js +41 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +203 -13
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +68 -5
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +15 -1
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +55 -29
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +37 -1
- package/dist/components/form/engine/optionActions.d.ts.map +1 -1
- package/dist/components/form/engine/optionActions.js +71 -1
- package/dist/components/form/engine/optionActions.js.map +1 -1
- package/dist/components/form/engine/optionsContext.d.ts +22 -0
- package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
- package/dist/components/form/engine/optionsContext.js +20 -0
- package/dist/components/form/engine/optionsContext.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +42 -1
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +76 -8
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +42 -3
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
- package/dist/components/form/engine/rendererTypes.js +3 -1
- package/dist/components/form/engine/rendererTypes.js.map +1 -1
- package/dist/components/form/engine/rowFocus.d.ts +30 -0
- package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
- package/dist/components/form/engine/rowFocus.js +93 -0
- package/dist/components/form/engine/rowFocus.js.map +1 -0
- package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
- package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
- package/dist/components/form/engine/rowMenuContext.js +103 -0
- package/dist/components/form/engine/rowMenuContext.js.map +1 -0
- package/dist/components/form/engine/rowOpenPicker.d.ts +27 -0
- package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
- package/dist/components/form/engine/rowOpenPicker.js +30 -0
- package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
- package/dist/components/form/engine/typeChoices.d.ts +42 -0
- package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
- package/dist/components/form/engine/typeChoices.js +33 -0
- package/dist/components/form/engine/typeChoices.js.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.js +53 -0
- package/dist/components/form/expressions/DpqlRendering.js.map +1 -0
- package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
- package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
- package/dist/components/form/expressions/ExpressionField.js +203 -45
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
- package/dist/components/form/expressions/argumentPresence.js +217 -0
- package/dist/components/form/expressions/argumentPresence.js.map +1 -0
- package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
- package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts +15 -2
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +21 -65
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
- package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
- package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/mockExpressions.js +8 -4
- package/dist/components/form/expressions/mockExpressions.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +95 -19
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/expressions/types.d.ts +17 -2
- package/dist/components/form/expressions/types.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +5 -2
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
- package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.js +50 -126
- package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts +21 -0
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +54 -8
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +52 -8
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +91 -28
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/date/Date.d.ts +1 -1
- package/dist/components/form/fields/date/Date.d.ts.map +1 -1
- package/dist/components/form/fields/date/Date.js +16 -2
- package/dist/components/form/fields/date/Date.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +14 -2
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/number/Number.d.ts +12 -1
- package/dist/components/form/fields/number/Number.d.ts.map +1 -1
- package/dist/components/form/fields/number/Number.js +6 -3
- package/dist/components/form/fields/number/Number.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +153 -12
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +120 -20
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +200 -7
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts +34 -2
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +606 -58
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
- package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
- package/dist/components/form/fields/template/TemplateText.js +80 -0
- package/dist/components/form/fields/template/TemplateText.js.map +1 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
- package/dist/components/smartEditor/SmartEditor.js +29 -8
- package/dist/components/smartEditor/SmartEditor.js.map +1 -1
- package/dist/components/smartEditor/useLspSession.d.ts +5 -1
- package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspSession.js +16 -2
- package/dist/components/smartEditor/useLspSession.js.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +7 -3
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/dpqlDetection.d.ts +58 -0
- package/dist/helpers/dpqlDetection.d.ts.map +1 -0
- package/dist/helpers/dpqlDetection.js +164 -0
- package/dist/helpers/dpqlDetection.js.map +1 -0
- package/dist/helpers/optionUiTypes.d.ts +31 -3
- package/dist/helpers/optionUiTypes.d.ts.map +1 -1
- package/dist/helpers/optionUiTypes.js +36 -1
- package/dist/helpers/optionUiTypes.js.map +1 -1
- package/dist/helpers/options.d.ts +57 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +97 -1
- package/dist/helpers/options.js.map +1 -1
- package/dist/helpers/templateItems.d.ts +47 -0
- package/dist/helpers/templateItems.d.ts.map +1 -0
- package/dist/helpers/templateItems.js +88 -0
- package/dist/helpers/templateItems.js.map +1 -0
- package/dist/helpers/templateText.d.ts +80 -0
- package/dist/helpers/templateText.d.ts.map +1 -0
- package/dist/helpers/templateText.js +291 -0
- package/dist/helpers/templateText.js.map +1 -0
- package/dist/helpers/templates.d.ts +30 -12
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +91 -33
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts +35 -8
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +162 -25
- package/dist/helpers/validations.js.map +1 -1
- package/dist/hooks/useArgSchema.d.ts +2 -1
- package/dist/hooks/useArgSchema.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.d.ts +3 -1
- package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.js +47 -9
- package/dist/hooks/useFetch/useFetch.js.map +1 -1
- package/dist/hooks/useTemplates.d.ts +20 -0
- package/dist/hooks/useTemplates.d.ts.map +1 -1
- package/dist/hooks/useTemplates.js +34 -2
- package/dist/hooks/useTemplates.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/fetch.d.ts +151 -1
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +256 -44
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/lspClient.d.ts +6 -0
- package/dist/utils/lspClient.d.ts.map +1 -1
- package/dist/utils/lspClient.js +62 -11
- package/dist/utils/lspClient.js.map +1 -1
- package/dist/utils/websocket.d.ts +6 -0
- package/dist/utils/websocket.d.ts.map +1 -1
- package/dist/utils/websocket.js +15 -7
- package/dist/utils/websocket.js.map +1 -1
- package/package.json +10 -6
- package/src/components/composer/Composer.tsx +7 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
- package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
- package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
- package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
- package/src/components/dpqlEditor/dpqlTags.ts +13 -0
- package/src/components/dpqlEditor/index.ts +4 -0
- package/src/components/dpqlEditor/types.ts +38 -2
- package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
- package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
- package/src/components/form/engine/CompactRow.tsx +487 -160
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +1269 -201
- package/src/components/form/engine/FormEngine.tsx +922 -270
- package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
- package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
- package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
- package/src/components/form/engine/compactRowContext.ts +15 -6
- package/src/components/form/engine/compactRowStyles.ts +56 -2
- package/src/components/form/engine/compactToolbarContext.ts +1 -1
- package/src/components/form/engine/optionActions.ts +89 -1
- package/src/components/form/engine/optionsContext.ts +24 -0
- package/src/components/form/engine/readFirst.ts +88 -8
- package/src/components/form/engine/rendererTypes.ts +48 -4
- package/src/components/form/engine/rowFocus.ts +101 -0
- package/src/components/form/engine/rowMenuContext.ts +159 -0
- package/src/components/form/engine/rowOpenPicker.ts +27 -0
- package/src/components/form/engine/typeChoices.ts +63 -0
- package/src/components/form/expressions/DpqlRendering.tsx +85 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
- package/src/components/form/expressions/ExpressionField.tsx +269 -55
- package/src/components/form/expressions/argumentPresence.ts +213 -0
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +39 -81
- package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
- package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
- package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
- package/src/components/form/expressions/mockExpressions.ts +8 -4
- package/src/components/form/expressions/renderExpressionToText.ts +112 -22
- package/src/components/form/expressions/types.ts +17 -2
- package/src/components/form/expressions/useExpressions.ts +5 -1
- package/src/components/form/expressions/useRenderExpression.ts +43 -127
- package/src/components/form/fields/Field.tsx +102 -20
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
- package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
- package/src/components/form/fields/date/Date.tsx +17 -1
- package/src/components/form/fields/long-string/LongString.tsx +15 -3
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
- package/src/components/form/fields/number/Number.tsx +25 -0
- package/src/components/form/fields/rich-text/RichText.tsx +248 -30
- package/src/components/form/fields/select/Select.stories.tsx +392 -0
- package/src/components/form/fields/select/Select.tsx +164 -27
- package/src/components/form/fields/select/SelectCollection.tsx +350 -6
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
- package/src/components/form/fields/template/TemplateField.tsx +825 -131
- package/src/components/form/fields/template/TemplateText.tsx +115 -0
- package/src/components/form/index.tsx +4 -0
- package/src/components/log/Log.stories.tsx +2 -1
- package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
- package/src/components/smartEditor/SmartEditor.tsx +41 -9
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
- package/src/components/smartEditor/useLspSession.ts +21 -3
- package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
- package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
- package/src/helpers/common.ts +7 -3
- package/src/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/optionUiTypes.ts +38 -0
- package/src/helpers/options.ts +96 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templateText.ts +350 -0
- package/src/helpers/templates.ts +94 -41
- package/src/helpers/validations.ts +203 -51
- package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
- package/src/hooks/useFetch/useFetch.tsx +53 -7
- package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
- package/src/hooks/useTemplates.ts +41 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
- package/src/index.tsx +14 -1
- package/src/stores/currentUser/currentUser.stories.tsx +2 -1
- package/src/stories/storyNetwork.ts +92 -0
- package/src/utils/fetch.ts +311 -21
- package/src/utils/lspClient.ts +68 -11
- package/src/utils/websocket.ts +20 -9
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
- package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
- package/dist/stories/Data/fileFieldArgSchema.json +0 -86
|
@@ -1,51 +1,36 @@
|
|
|
1
1
|
// Copyright 2026 Qore Technologies, s.r.o.
|
|
2
|
-
// The "Explain" seam. Turns an expression AST into
|
|
2
|
+
// The "Explain" seam. Turns an expression AST into its readable rendering.
|
|
3
3
|
//
|
|
4
|
-
// The
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// `LspSharedConnection`, and JSON-RPC request ids associate each response
|
|
9
|
-
//
|
|
10
|
-
//
|
|
4
|
+
// The rendering is the server's `DataProvider::renderExpression`, reached over
|
|
5
|
+
// the LSP custom method `dpql/renderExpression` (added next to `dpql/serialize`
|
|
6
|
+
// in `QorusLspWebSocketHandler.qc`). All consumers share ONE render socket: the
|
|
7
|
+
// module-level `ReqraftLspClient` below rides the per-endpoint
|
|
8
|
+
// `LspSharedConnection`, and JSON-RPC request ids associate each response with
|
|
9
|
+
// the caller — N expression builders on one form means N independent in-flight
|
|
10
|
+
// requests on one connection, no cross-wiring.
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// There is no client-side stand-in. A Qorus form does not work without Qorus,
|
|
13
|
+
// so the only time the server was "unreachable" in practice was while the
|
|
14
|
+
// page's first connection was still opening — and racing that with a 1.5s timer
|
|
15
|
+
// replaced the real rendering with an approximation labelled "Approximate", on
|
|
16
|
+
// exactly the first Explain of every page. A rendering now waits for the
|
|
17
|
+
// connection; `null` means the server cannot render at all (it predates the
|
|
18
|
+
// method, or the connection gave up), which the caller says plainly.
|
|
15
19
|
import { nanoid } from 'nanoid';
|
|
16
|
-
import { useCallback, useEffect, useRef
|
|
20
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
17
21
|
import { ReqraftLspClient } from '../../../utils/lspClient';
|
|
18
|
-
import {
|
|
22
|
+
import { IExpressionValue } from './types';
|
|
19
23
|
|
|
24
|
+
/* The synchronous summary a collapsed row prints — see `readFirst.ts`. It is
|
|
25
|
+
not a stand-in for this rendering and is never shown in its place. */
|
|
20
26
|
export { renderExpressionToText } from './renderExpressionToText';
|
|
21
|
-
import { renderExpressionToText } from './renderExpressionToText';
|
|
22
|
-
|
|
23
|
-
// ── The shared render transport ───────────────────────────────────────
|
|
24
|
-
// One lazy module-level client for ALL `useRenderExpression` consumers.
|
|
25
|
-
// Rendering needs no document/context state, so a single connection (shared
|
|
26
|
-
// further with any open DPQL editors via `LspSharedConnection`) serves
|
|
27
|
-
// every builder on the page.
|
|
28
27
|
|
|
29
28
|
/** Coalesce rapid re-renders per consumer (live typing) — trailing edge. */
|
|
30
29
|
const RENDER_DEBOUNCE_MS = 300;
|
|
31
|
-
/** How long a render call waits for the socket before falling back. */
|
|
32
|
-
const CONNECT_WAIT_MS = 1500;
|
|
33
|
-
/** After a failed wait, fall back instantly for this long before re-waiting. */
|
|
34
|
-
const CONNECT_RETRY_COOLDOWN_MS = 10000;
|
|
35
30
|
|
|
36
31
|
let renderClient: ReqraftLspClient | null = null;
|
|
37
32
|
/** Set after a `-32601 method not found` — the server predates the method. */
|
|
38
33
|
let methodUnsupported = false;
|
|
39
|
-
let lastConnectWaitFailedAt = 0;
|
|
40
|
-
|
|
41
|
-
let lastServerRendering = false;
|
|
42
|
-
const serverRenderingListeners = new Set<(on: boolean) => void>();
|
|
43
|
-
const setLastServerRendering = (on: boolean): void => {
|
|
44
|
-
if (on !== lastServerRendering) {
|
|
45
|
-
lastServerRendering = on;
|
|
46
|
-
serverRenderingListeners.forEach((listener) => listener(on));
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
34
|
|
|
50
35
|
const getRenderClient = (): ReqraftLspClient => {
|
|
51
36
|
if (!renderClient) {
|
|
@@ -59,55 +44,24 @@ const getRenderClient = (): ReqraftLspClient => {
|
|
|
59
44
|
return renderClient;
|
|
60
45
|
};
|
|
61
46
|
|
|
62
|
-
/**
|
|
63
|
-
* Wait (briefly) for the shared connection. `connect()` is re-entrant and the
|
|
64
|
-
* underlying socket keeps its own reconnect schedule, so this only bounds how
|
|
65
|
-
* long a single render call blocks before falling back — after a failed wait
|
|
66
|
-
* we fall back instantly for a cooldown period instead of re-blocking.
|
|
67
|
-
*/
|
|
68
|
-
const waitForConnection = async (client: ReqraftLspClient): Promise<boolean> => {
|
|
69
|
-
if (client.isConnected) return true;
|
|
70
|
-
if (Date.now() - lastConnectWaitFailedAt < CONNECT_RETRY_COOLDOWN_MS) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
const connected = await Promise.race([
|
|
74
|
-
client.connect().then(
|
|
75
|
-
() => true,
|
|
76
|
-
() => false
|
|
77
|
-
),
|
|
78
|
-
new Promise<boolean>((resolve) => {
|
|
79
|
-
setTimeout(() => resolve(false), CONNECT_WAIT_MS);
|
|
80
|
-
}),
|
|
81
|
-
]);
|
|
82
|
-
if (!connected || !client.isConnected) {
|
|
83
|
-
lastConnectWaitFailedAt = Date.now();
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
return true;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
47
|
/** One rendered expression — what `renderRich` resolves with. */
|
|
90
48
|
export interface IRenderedExpression {
|
|
91
49
|
/** The readable rendering (plain text). */
|
|
92
50
|
text: string;
|
|
93
51
|
/**
|
|
94
52
|
* The server's richtext form — template references arrive as `tag` nodes
|
|
95
|
-
* (the IDE renders them as chips).
|
|
96
|
-
* Convert with `richtextResponseToSlate` for `RichTextFormField`.
|
|
53
|
+
* (the IDE renders them as chips). Convert with `richtextResponseToSlate`.
|
|
97
54
|
*/
|
|
98
55
|
richtext: unknown | null;
|
|
99
|
-
/** `true` when the server `dpql/renderExpression` produced this result. */
|
|
100
|
-
server: boolean;
|
|
101
56
|
}
|
|
102
57
|
|
|
103
|
-
|
|
104
|
-
const renderViaServer = async (
|
|
105
|
-
value: IExpressionValue
|
|
106
|
-
): Promise<{ text: string; richtext: unknown | null } | null> => {
|
|
58
|
+
const renderViaServer = async (value: IExpressionValue): Promise<IRenderedExpression | null> => {
|
|
107
59
|
if (methodUnsupported) return null;
|
|
108
60
|
const client = getRenderClient();
|
|
109
|
-
if (!(await waitForConnection(client))) return null;
|
|
110
61
|
try {
|
|
62
|
+
// Waits as long as the connection does: it has its own reconnect schedule
|
|
63
|
+
// and rejects once it gives up, so this never outlives a dead socket.
|
|
64
|
+
await client.connect();
|
|
111
65
|
const result = await client.customRequest<{ rendered?: string; richtext?: unknown }>(
|
|
112
66
|
'dpql/renderExpression',
|
|
113
67
|
// The server accepts both the bare `{exp, args}` AST and the
|
|
@@ -131,75 +85,50 @@ export const _resetRenderExpressionTransportForTests = (): void => {
|
|
|
131
85
|
renderClient?.disconnect();
|
|
132
86
|
renderClient = null;
|
|
133
87
|
methodUnsupported = false;
|
|
134
|
-
lastConnectWaitFailedAt = 0;
|
|
135
|
-
lastServerRendering = false;
|
|
136
88
|
};
|
|
137
89
|
|
|
138
90
|
interface IPendingRender {
|
|
139
91
|
value: IExpressionValue;
|
|
140
|
-
|
|
141
|
-
resolvers: Array<(rendered: IRenderedExpression) => void>;
|
|
92
|
+
resolvers: Array<(rendered: IRenderedExpression | null) => void>;
|
|
142
93
|
}
|
|
143
94
|
|
|
144
95
|
export interface IUseRenderExpressionResult {
|
|
145
|
-
/**
|
|
146
|
-
render: (value: IExpressionValue
|
|
96
|
+
/** The readable rendering of an expression AST, or `null` when the server cannot render. */
|
|
97
|
+
render: (value: IExpressionValue) => Promise<string | null>;
|
|
147
98
|
/**
|
|
148
99
|
* Like `render`, but also resolves the server's richtext form (template
|
|
149
|
-
* references as `tag` nodes — chips)
|
|
150
|
-
*
|
|
151
|
-
*/
|
|
152
|
-
renderRich: (
|
|
153
|
-
value: IExpressionValue,
|
|
154
|
-
expressions?: IExpressionSchema[]
|
|
155
|
-
) => Promise<IRenderedExpression>;
|
|
156
|
-
/**
|
|
157
|
-
* `true` when the server `dpql/renderExpression` path served the last
|
|
158
|
-
* render; `false` while on the client-side approximation (LSP unreachable
|
|
159
|
-
* or the server predates the method).
|
|
100
|
+
* references as `tag` nodes — chips). `null` when there is nothing to render
|
|
101
|
+
* or the server cannot render it.
|
|
160
102
|
*/
|
|
161
|
-
|
|
103
|
+
renderRich: (value: IExpressionValue) => Promise<IRenderedExpression | null>;
|
|
162
104
|
}
|
|
163
105
|
|
|
164
106
|
export const useRenderExpression = (): IUseRenderExpressionResult => {
|
|
165
|
-
const [serverRendering, setServerRendering] = useState(lastServerRendering);
|
|
166
107
|
const pendingRef = useRef<IPendingRender | null>(null);
|
|
167
108
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
168
109
|
const lastRunRef = useRef(0);
|
|
169
110
|
|
|
170
|
-
useEffect(
|
|
171
|
-
|
|
172
|
-
return () => {
|
|
173
|
-
serverRenderingListeners.delete(setServerRendering);
|
|
111
|
+
useEffect(
|
|
112
|
+
() => () => {
|
|
174
113
|
if (timerRef.current) {
|
|
175
114
|
clearTimeout(timerRef.current);
|
|
176
115
|
timerRef.current = null;
|
|
177
116
|
}
|
|
178
|
-
// Settle any coalesced callers
|
|
179
|
-
// promise is left hanging past unmount.
|
|
117
|
+
// Settle any coalesced callers so no promise is left hanging past unmount.
|
|
180
118
|
const pending = pendingRef.current;
|
|
181
119
|
pendingRef.current = null;
|
|
182
|
-
pending?.resolvers.forEach((resolve) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
server: false,
|
|
187
|
-
})
|
|
188
|
-
);
|
|
189
|
-
};
|
|
190
|
-
}, []);
|
|
120
|
+
pending?.resolvers.forEach((resolve) => resolve(null));
|
|
121
|
+
},
|
|
122
|
+
[]
|
|
123
|
+
);
|
|
191
124
|
|
|
192
125
|
const renderRich = useCallback(
|
|
193
|
-
(
|
|
194
|
-
|
|
195
|
-
expressions: IExpressionSchema[] = []
|
|
196
|
-
): Promise<IRenderedExpression> =>
|
|
197
|
-
new Promise<IRenderedExpression>((resolve) => {
|
|
126
|
+
(value: IExpressionValue): Promise<IRenderedExpression | null> =>
|
|
127
|
+
new Promise<IRenderedExpression | null>((resolve) => {
|
|
198
128
|
// Coalesce calls per consumer: rapid live updates collapse to the
|
|
199
129
|
// newest AST; every awaiting caller gets that newest rendering.
|
|
200
130
|
pendingRef.current = {
|
|
201
131
|
value,
|
|
202
|
-
expressions,
|
|
203
132
|
resolvers: [...(pendingRef.current?.resolvers ?? []), resolve],
|
|
204
133
|
};
|
|
205
134
|
|
|
@@ -208,20 +137,7 @@ export const useRenderExpression = (): IUseRenderExpressionResult => {
|
|
|
208
137
|
pendingRef.current = null;
|
|
209
138
|
if (!job) return;
|
|
210
139
|
lastRunRef.current = Date.now();
|
|
211
|
-
|
|
212
|
-
if (!job.value?.exp) {
|
|
213
|
-
outcome = { text: '', richtext: null, server: false };
|
|
214
|
-
} else {
|
|
215
|
-
const served = await renderViaServer(job.value);
|
|
216
|
-
setLastServerRendering(served !== null);
|
|
217
|
-
outcome = served
|
|
218
|
-
? { ...served, server: true }
|
|
219
|
-
: {
|
|
220
|
-
text: renderExpressionToText(job.value, job.expressions),
|
|
221
|
-
richtext: null,
|
|
222
|
-
server: false,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
140
|
+
const outcome = job.value?.exp ? await renderViaServer(job.value) : null;
|
|
225
141
|
job.resolvers.forEach((r) => r(outcome));
|
|
226
142
|
};
|
|
227
143
|
|
|
@@ -240,10 +156,10 @@ export const useRenderExpression = (): IUseRenderExpressionResult => {
|
|
|
240
156
|
);
|
|
241
157
|
|
|
242
158
|
const render = useCallback(
|
|
243
|
-
(value: IExpressionValue
|
|
244
|
-
renderRich(value
|
|
159
|
+
(value: IExpressionValue): Promise<string | null> =>
|
|
160
|
+
renderRich(value).then((r) => r?.text ?? null),
|
|
245
161
|
[renderRich]
|
|
246
162
|
);
|
|
247
163
|
|
|
248
|
-
return { render, renderRich
|
|
164
|
+
return { render, renderRich };
|
|
249
165
|
};
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
IReqoreLabelProps,
|
|
3
3
|
ReqoreCheckbox,
|
|
4
4
|
ReqoreControlGroup,
|
|
5
|
-
ReqoreSkeleton,
|
|
6
5
|
ReqoreSpan,
|
|
7
6
|
} from '@qoretechnologies/reqore';
|
|
8
7
|
import { IWithReqoreSize } from '@qoretechnologies/reqore/dist/types/global';
|
|
@@ -26,7 +25,15 @@ import { ReqraftObjectFormField } from './object/Object';
|
|
|
26
25
|
import { RichTextFormField } from './rich-text/RichText';
|
|
27
26
|
import { rendersCreatableValueSelect } from './allowed-values/AllowedValues';
|
|
28
27
|
import { SelectFormField } from './select/Select';
|
|
29
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
getSelectItemShortDescription,
|
|
30
|
+
getSelectItemUnavailability,
|
|
31
|
+
ISelectFieldCollectionItem,
|
|
32
|
+
ISelectFieldCollectionItemAction,
|
|
33
|
+
TSelectFieldCollectionItemTooltip,
|
|
34
|
+
UNAVAILABLE_ITEM_CLASS,
|
|
35
|
+
} from './select/SelectCollection';
|
|
36
|
+
import { useAllowedValueAvailability } from '../engine/OptionFieldMessages';
|
|
30
37
|
import { StringFormField } from './string/String';
|
|
31
38
|
import { ArrayAutoField } from './array/ArrayAutoField';
|
|
32
39
|
import { AutoFormField } from './auto/AutoFormField';
|
|
@@ -38,6 +45,7 @@ import { IDataSchemaDefinition } from './schema-definition/types';
|
|
|
38
45
|
// Direct import — circular dep (FormEngine → TemplateField → FormField → FormEngine) is safe
|
|
39
46
|
// because modules are all loaded before any component renders
|
|
40
47
|
import { FormEngine } from '../engine/FormEngine';
|
|
48
|
+
import { FormFieldsSkeleton } from '../engine/FormFieldsSkeleton';
|
|
41
49
|
|
|
42
50
|
/** Soft Qore types (softint, softstring, …) render as their concrete base —
|
|
43
51
|
* the soft/hard distinction is value-level, not UI. */
|
|
@@ -68,6 +76,27 @@ const mapQorusTypeToFormFieldType = (type: string): TFormFieldType => {
|
|
|
68
76
|
// Re-export for consumers who need to construct allowed values
|
|
69
77
|
export type { IQorusAllowedValue };
|
|
70
78
|
|
|
79
|
+
/**
|
|
80
|
+
* An allowed value, with the keys reqraft's pickers read.
|
|
81
|
+
*
|
|
82
|
+
* ts-toolkit 0.5.82 now declares all three on `IQorusAllowedValue`, so this is no
|
|
83
|
+
* longer a placeholder waiting on a publish. It stays because two of the three are
|
|
84
|
+
* WIDER here on purpose: the shared types are JSON-pure, since a form schema has to
|
|
85
|
+
* survive the wire, while reqraft renders in React and lets a consumer hand it a
|
|
86
|
+
* live `React.ElementType` for a tooltip's content or an action. Collapsing onto the
|
|
87
|
+
* shared type would quietly take that away from every consumer. `depends_on` is the
|
|
88
|
+
* same shape either way. Every key is optional, so a plain `IQorusAllowedValue` still
|
|
89
|
+
* is one.
|
|
90
|
+
*/
|
|
91
|
+
export interface IReqraftAllowedValue extends IQorusAllowedValue {
|
|
92
|
+
/** Offered only while every entry holds — a FIELD's `depends_on`, one level down. */
|
|
93
|
+
depends_on?: (string | string[])[];
|
|
94
|
+
/** A tooltip on the value's whole row in the picker. */
|
|
95
|
+
tooltip?: TSelectFieldCollectionItemTooltip;
|
|
96
|
+
/** Affordances inline with the value's name, where the row already has the height. */
|
|
97
|
+
title_actions?: ISelectFieldCollectionItemAction[];
|
|
98
|
+
}
|
|
99
|
+
|
|
71
100
|
export interface IFormFieldProps<T extends TFormFieldType = TFormFieldType> extends Omit<
|
|
72
101
|
IQorusFormFieldSchemaBase,
|
|
73
102
|
'value' | 'default_value' | 'arg_schema' | 'element_type' | 'ui_element_type'
|
|
@@ -345,7 +374,8 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
345
374
|
|
|
346
375
|
case 'hash': {
|
|
347
376
|
if (argSchemaLoading) {
|
|
348
|
-
|
|
377
|
+
// A hash renders a nested FORM, so it waits in the shape of one.
|
|
378
|
+
return <FormFieldsSkeleton rows={2} />;
|
|
349
379
|
}
|
|
350
380
|
if (finalArgSchema) {
|
|
351
381
|
return (
|
|
@@ -522,23 +552,47 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
522
552
|
}
|
|
523
553
|
};
|
|
524
554
|
|
|
555
|
+
/* Resolved once for the whole list, against the form this field stands in:
|
|
556
|
+
the value's own `depends_on`, and a refusal the server had already decided.
|
|
557
|
+
Values that gate on nothing cost nothing — see the hook. */
|
|
558
|
+
const availability = useAllowedValueAvailability(allowed_values as IReqraftAllowedValue[]);
|
|
559
|
+
|
|
525
560
|
/**
|
|
526
561
|
* Maps an IQorusAllowedValue to the shape SelectFormField / checkboxes expect.
|
|
527
562
|
* The actual selectable value lives at item.value.value (the wrapper has type metadata).
|
|
563
|
+
*
|
|
564
|
+
* The list used to be a whitelist of eleven keys, which silently dropped the
|
|
565
|
+
* row affordances (`tooltip`, `title_actions`) and `metadata` — so a host that
|
|
566
|
+
* built them, as the IDE's kind picker does, had to hand its items straight to
|
|
567
|
+
* `SelectFormField` and bypass this renderer altogether to keep them.
|
|
528
568
|
*/
|
|
529
|
-
const mapAllowedValue = (item: IQorusAllowedValue) =>
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
569
|
+
const mapAllowedValue = (item: IQorusAllowedValue, index = -1): ISelectFieldCollectionItem => {
|
|
570
|
+
const extended = item as IReqraftAllowedValue;
|
|
571
|
+
const resolved = availability[index];
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
display_name: item.display_name,
|
|
575
|
+
short_desc: item.short_desc,
|
|
576
|
+
desc: item.desc,
|
|
577
|
+
icon: item.icon,
|
|
578
|
+
image: item.image,
|
|
579
|
+
badge: item.badge as TReqoreBadge,
|
|
580
|
+
intent: item.intent,
|
|
581
|
+
messages: item.messages,
|
|
582
|
+
actions: item.actions,
|
|
583
|
+
title_actions: extended.title_actions,
|
|
584
|
+
tooltip: extended.tooltip,
|
|
585
|
+
metadata: item.metadata,
|
|
586
|
+
// Both sources of a refusal end up here, as one state: the click handler
|
|
587
|
+
// is dropped and the reason is printed, whichever of them decided.
|
|
588
|
+
disabled: item.disabled || resolved?.available === false,
|
|
589
|
+
unavailable:
|
|
590
|
+
resolved && !resolved.available ?
|
|
591
|
+
{ title: resolved.title, content: resolved.reason as string, intent: resolved.intent }
|
|
592
|
+
: undefined,
|
|
593
|
+
value: item.value?.value,
|
|
594
|
+
};
|
|
595
|
+
};
|
|
542
596
|
|
|
543
597
|
/**
|
|
544
598
|
* Renders the allowed-values picker.
|
|
@@ -607,17 +661,45 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
607
661
|
{allowed_values.map((item, index) => {
|
|
608
662
|
const itemValue = item.value?.value;
|
|
609
663
|
const checked = isEqual(value, itemValue);
|
|
664
|
+
const mapped = mapAllowedValue(item, index);
|
|
665
|
+
const unavailable = getSelectItemUnavailability(mapped);
|
|
610
666
|
return (
|
|
611
667
|
<ReqoreCheckbox
|
|
612
668
|
margin='right'
|
|
613
669
|
key={index}
|
|
614
670
|
label={item.display_name ?? JSON.stringify(itemValue)}
|
|
615
|
-
tooltip={getSelectItemShortDescription(
|
|
616
|
-
|
|
671
|
+
tooltip={getSelectItemShortDescription(mapped)}
|
|
672
|
+
/* The reason is IN the row, not only on its hover: a checkbox
|
|
673
|
+
has no body to bury it in, and a hover is the one affordance
|
|
674
|
+
a touch screen and a keyboard never reach. */
|
|
675
|
+
description={unavailable?.content}
|
|
676
|
+
className={unavailable ? UNAVAILABLE_ITEM_CLASS : undefined}
|
|
677
|
+
/* Never reqore's `disabled` — it applies `DisabledElement`
|
|
678
|
+
(`pointer-events: none`), which would take the tooltip with
|
|
679
|
+
it. Dropping the handler is what makes the box inert; the
|
|
680
|
+
`disabled` this used to pass was the ITEM's, and it was the
|
|
681
|
+
one path in the picker family that destroyed its own
|
|
682
|
+
explanation. */
|
|
683
|
+
/* `labelEffect`, not `effect`: a non-switch `ReqoreCheckbox`
|
|
684
|
+
destructures `effect` out and never applies it — it reaches
|
|
685
|
+
only the switch rendering — so the dimming this asked for
|
|
686
|
+
never appeared at all. And the NAME is the right thing to
|
|
687
|
+
dim: `description` carries the reason, and a reason has to
|
|
688
|
+
stay readable. Same rule the collection rows follow. */
|
|
689
|
+
labelEffect={unavailable ? { opacity: 0.55 } : undefined}
|
|
690
|
+
/* `cursor: not-allowed` from reqore's own `ReadOnlyElement`
|
|
691
|
+
rather than a hand-rolled inline style. It leaves the
|
|
692
|
+
pointer events intact, which is what the tooltip is read
|
|
693
|
+
through; it does NOT withhold `onClick`, but this row
|
|
694
|
+
already withholds it below, so it is purely additive. */
|
|
695
|
+
readOnly={!!unavailable}
|
|
696
|
+
uncheckedIcon={unavailable ? 'LockLine' : undefined}
|
|
617
697
|
checked={checked}
|
|
618
698
|
intent={checked ? 'info' : undefined}
|
|
619
|
-
onClick={
|
|
620
|
-
|
|
699
|
+
onClick={
|
|
700
|
+
unavailable ? undefined : (
|
|
701
|
+
() => handleChange((checked ? undefined : itemValue) as TFormFieldValueType<T>)
|
|
702
|
+
)
|
|
621
703
|
}
|
|
622
704
|
/>
|
|
623
705
|
);
|
|
@@ -13,8 +13,14 @@ import { IReqoreButtonProps } from '@qoretechnologies/reqore/dist/components/But
|
|
|
13
13
|
import { IQorusAllowedValue, TQorusType } from '@qoretechnologies/ts-toolkit';
|
|
14
14
|
import { size as count, isEqual } from 'lodash';
|
|
15
15
|
import { memo, useMemo } from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
getSelectItemShortDescription,
|
|
18
|
+
getSelectItemUnavailability,
|
|
19
|
+
UNAVAILABLE_ITEM_CLASS,
|
|
20
|
+
} from '../select/SelectCollection';
|
|
17
21
|
import { ISelectFormFieldItem, SelectFormField as Select } from '../select/Select';
|
|
22
|
+
import { useAllowedValueAvailability } from '../../engine/OptionFieldMessages';
|
|
23
|
+
import type { IReqraftAllowedValue } from '../Field';
|
|
18
24
|
|
|
19
25
|
export interface IFieldAllowedValuesProps extends Pick<
|
|
20
26
|
IReqoreButtonProps,
|
|
@@ -40,13 +46,23 @@ const DISALLOW_COMPACT_AO_TYPES = ['connection'];
|
|
|
40
46
|
* Whether a creatable field's value is carried by the chip picker rather than
|
|
41
47
|
* by a raw editor with a suggestion list beside it.
|
|
42
48
|
*
|
|
43
|
-
*
|
|
49
|
+
* String-valued only, and only with something to suggest. Every other shape a
|
|
44
50
|
* creatable field can declare either does not fit a one-line chip (a document
|
|
45
51
|
* — `long-string`, `binary`, `data`; a hash; a list) or would come back out of
|
|
46
52
|
* one as a string (a number, a date), and a field with nothing to offer is
|
|
47
53
|
* better served by the editor it has always had. Those keep today's rendering:
|
|
48
54
|
* the raw editor, with the saved-and-suggested picker under it.
|
|
49
55
|
*
|
|
56
|
+
* `select-string` counts, and leaving it out was the bug. A picker field
|
|
57
|
+
* declares its storage type (`*string`) and its `ui_type` (`select-string`),
|
|
58
|
+
* and by the time a schema reaches here the two have been flattened to one:
|
|
59
|
+
* `type` arrives as `select-string`. So this returned false for every such
|
|
60
|
+
* field and NEITHER renderer stood down — the picker drew the value, and the
|
|
61
|
+
* saved-and-suggested list drew the same candidates again underneath it. A
|
|
62
|
+
* test's Service and Method fields showed 183 and 7 items twice over, in two
|
|
63
|
+
* controls that set one value. A select-string's value IS a string and fits the
|
|
64
|
+
* chip, which is the only thing this predicate is really asking.
|
|
65
|
+
*
|
|
50
66
|
* Both the renderer of the picker (`FieldAllowedValues`) and the renderer of
|
|
51
67
|
* the raw editor (`AutoFormField`, `FormField`) ask this, so that exactly one
|
|
52
68
|
* of them draws the value.
|
|
@@ -55,7 +71,8 @@ export const rendersCreatableValueSelect = (
|
|
|
55
71
|
type?: string,
|
|
56
72
|
allowCreation?: boolean,
|
|
57
73
|
items?: unknown[]
|
|
58
|
-
): boolean =>
|
|
74
|
+
): boolean =>
|
|
75
|
+
!!allowCreation && (type === 'string' || type === 'select-string') && count(items) > 0;
|
|
59
76
|
|
|
60
77
|
export const FieldAllowedValuesCheckGroup = memo(
|
|
61
78
|
({
|
|
@@ -87,44 +104,71 @@ export const FieldAllowedValuesCheckGroup = memo(
|
|
|
87
104
|
: multiSelect ? 'Select one or more:'
|
|
88
105
|
: 'Select one:'}
|
|
89
106
|
</ReqoreSpan>
|
|
90
|
-
{items?.map((item) =>
|
|
91
|
-
|
|
92
|
-
margin='right'
|
|
93
|
-
key={item.value?.toString()}
|
|
94
|
-
label={item.display_name || JSON.stringify(item.value)}
|
|
95
|
-
tooltip={getSelectItemShortDescription(item)}
|
|
96
|
-
disabled={rest.disabled}
|
|
97
|
-
readOnly={rest.readOnly}
|
|
98
|
-
intent={item.value === value ? 'info' : undefined}
|
|
99
|
-
// `isEqual`, and only once there is a value to match. The single
|
|
100
|
-
// -select branch compared JSON.stringify(value) with
|
|
101
|
-
// JSON.stringify(item.value), and JSON.stringify(undefined) is
|
|
102
|
-
// undefined — so an unset field whose items carry no resolved value
|
|
103
|
-
// compared undefined with undefined and reported EVERY option as
|
|
104
|
-
// checked, on a field that was simultaneously "This field is
|
|
105
|
-
// required". Nothing selected must read as nothing selected.
|
|
106
|
-
checked={
|
|
107
|
-
multiSelect
|
|
108
|
-
? Array.isArray(value) && value.some((v) => isEqual(v, item.value))
|
|
109
|
-
: value !== undefined && value !== null && isEqual(value, item.value)
|
|
110
|
-
}
|
|
111
|
-
onClick={() => {
|
|
112
|
-
if (multiSelect) {
|
|
113
|
-
let newValue: unknown[] = Array.isArray(value) ? [...value] : [];
|
|
114
|
-
|
|
115
|
-
if (newValue.some((v) => isEqual(v, item.value))) {
|
|
116
|
-
newValue = newValue.filter((v) => !isEqual(v, item.value));
|
|
117
|
-
} else {
|
|
118
|
-
newValue.push(item.value);
|
|
119
|
-
}
|
|
107
|
+
{items?.map((item) => {
|
|
108
|
+
const unavailable = getSelectItemUnavailability(item);
|
|
120
109
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
110
|
+
return (
|
|
111
|
+
<ReqoreCheckbox
|
|
112
|
+
margin='right'
|
|
113
|
+
key={item.value?.toString()}
|
|
114
|
+
label={item.display_name || JSON.stringify(item.value)}
|
|
115
|
+
tooltip={getSelectItemShortDescription(item)}
|
|
116
|
+
/* The reason is IN the row: a checkbox has no body to put it in,
|
|
117
|
+
and a hover is the one affordance a touch screen and a keyboard
|
|
118
|
+
never reach. */
|
|
119
|
+
description={unavailable?.content}
|
|
120
|
+
className={unavailable ? UNAVAILABLE_ITEM_CLASS : undefined}
|
|
121
|
+
/* `labelEffect`, not `effect`: a non-switch `ReqoreCheckbox`
|
|
122
|
+
destructures `effect` out and never applies it, so the dimming
|
|
123
|
+
this asked for never appeared. The NAME is also the right
|
|
124
|
+
thing to dim — `description` above carries the reason, and a
|
|
125
|
+
reason has to stay readable. */
|
|
126
|
+
labelEffect={unavailable ? { opacity: 0.55 } : undefined}
|
|
127
|
+
uncheckedIcon={unavailable ? 'LockLine' : undefined}
|
|
128
|
+
/* The FIELD's `disabled` still goes through reqore — a field nobody
|
|
129
|
+
may edit has no explanation to protect. A refused VALUE never
|
|
130
|
+
does: `DisabledElement` is `pointer-events: none` and would take
|
|
131
|
+
its tooltip with it. */
|
|
132
|
+
disabled={rest.disabled}
|
|
133
|
+
/* `cursor: not-allowed` from reqore's own `ReadOnlyElement`,
|
|
134
|
+
which keeps the pointer events the tooltip is read through.
|
|
135
|
+
It does not withhold `onClick` — the handler below does. */
|
|
136
|
+
readOnly={rest.readOnly || !!unavailable}
|
|
137
|
+
intent={item.value === value ? 'info' : undefined}
|
|
138
|
+
// `isEqual`, and only once there is a value to match. The single
|
|
139
|
+
// -select branch compared JSON.stringify(value) with
|
|
140
|
+
// JSON.stringify(item.value), and JSON.stringify(undefined) is
|
|
141
|
+
// undefined — so an unset field whose items carry no resolved value
|
|
142
|
+
// compared undefined with undefined and reported EVERY option as
|
|
143
|
+
// checked, on a field that was simultaneously "This field is
|
|
144
|
+
// required". Nothing selected must read as nothing selected.
|
|
145
|
+
checked={
|
|
146
|
+
multiSelect
|
|
147
|
+
? Array.isArray(value) && value.some((v) => isEqual(v, item.value))
|
|
148
|
+
: value !== undefined && value !== null && isEqual(value, item.value)
|
|
124
149
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
onClick={
|
|
151
|
+
unavailable ? undefined : (
|
|
152
|
+
() => {
|
|
153
|
+
if (multiSelect) {
|
|
154
|
+
let newValue: unknown[] = Array.isArray(value) ? [...value] : [];
|
|
155
|
+
|
|
156
|
+
if (newValue.some((v) => isEqual(v, item.value))) {
|
|
157
|
+
newValue = newValue.filter((v) => !isEqual(v, item.value));
|
|
158
|
+
} else {
|
|
159
|
+
newValue.push(item.value);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
onChange(name, newValue);
|
|
163
|
+
} else {
|
|
164
|
+
onChange(name, isEqual(value, item.value) ? undefined : item.value);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
})}
|
|
128
172
|
</ReqoreControlGroup>
|
|
129
173
|
);
|
|
130
174
|
}
|
|
@@ -146,17 +190,35 @@ export const FieldAllowedValues = memo(
|
|
|
146
190
|
readOnly,
|
|
147
191
|
label,
|
|
148
192
|
}: IFieldAllowedValuesProps) => {
|
|
193
|
+
/* Resolved against the form this field stands in — the value's own
|
|
194
|
+
`depends_on`, and a refusal the server had already decided. Both end up
|
|
195
|
+
as one state on the item: no click handler, and a reason to read. */
|
|
196
|
+
const availability = useAllowedValueAvailability(items as IReqraftAllowedValue[]);
|
|
197
|
+
|
|
149
198
|
const fullItems = useMemo(() => {
|
|
150
199
|
const result = [
|
|
151
200
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
152
|
-
...items.map(({ metadata, value, ...rest }) =>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
201
|
+
...items.map(({ metadata, value, ...rest }, index) => {
|
|
202
|
+
const resolved = availability[index];
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
value: value?.value,
|
|
206
|
+
...rest,
|
|
207
|
+
disabled: rest.disabled || resolved?.available === false,
|
|
208
|
+
unavailable:
|
|
209
|
+
resolved && !resolved.available ?
|
|
210
|
+
{
|
|
211
|
+
title: resolved.title,
|
|
212
|
+
content: resolved.reason as string,
|
|
213
|
+
intent: resolved.intent,
|
|
214
|
+
}
|
|
215
|
+
: undefined,
|
|
216
|
+
};
|
|
217
|
+
}),
|
|
156
218
|
] as ISelectFormFieldItem[];
|
|
157
219
|
|
|
158
220
|
return result;
|
|
159
|
-
}, [JSON.stringify(items), type, value, showSavedValues, disabled, readOnly]);
|
|
221
|
+
}, [JSON.stringify(items), availability, type, value, showSavedValues, disabled, readOnly]);
|
|
160
222
|
|
|
161
223
|
const style = useMemo(() => ({ width: '100%' }), []);
|
|
162
224
|
|