@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,14 +1,23 @@
|
|
|
1
1
|
import { ReqoreControlGroup, ReqoreTag, ReqoreVerticalSpacer } from '@qoretechnologies/reqore';
|
|
2
2
|
import { IReqoreTagProps } from '@qoretechnologies/reqore/dist/components/Tag';
|
|
3
|
+
import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
3
4
|
import { IQorusFormField, IQorusFormSchema, TQorusForm } from '@qoretechnologies/ts-toolkit';
|
|
4
|
-
import {
|
|
5
|
+
import { isArray, size } from 'lodash';
|
|
5
6
|
import { useMemo } from 'react';
|
|
6
|
-
import {
|
|
7
|
+
import { useContextSelector } from 'use-context-selector';
|
|
8
|
+
import {
|
|
9
|
+
UNAVAILABLE_VALUE_FALLBACK_REASON,
|
|
10
|
+
getRefusalMessage,
|
|
11
|
+
getRequiredOptionMessage,
|
|
12
|
+
} from '../../../helpers/options';
|
|
13
|
+
import { isUntypedOptionType } from '../../../helpers/optionUiTypes';
|
|
7
14
|
import {
|
|
8
15
|
hasAllDependenciesFullfilled,
|
|
16
|
+
parseDependency,
|
|
9
17
|
validateFieldWithResult,
|
|
10
18
|
validateOptionWithRequiredGroups,
|
|
11
19
|
} from '../../../helpers/validations';
|
|
20
|
+
import { OptionsContext } from './optionsContext';
|
|
12
21
|
|
|
13
22
|
/** A `messages` entry that appears only for certain values of its siblings. */
|
|
14
23
|
export interface IConditionalFieldMessage {
|
|
@@ -49,10 +58,13 @@ export const isConditionalMessageShown = (
|
|
|
49
58
|
optionsSchema?: IQorusFormSchema
|
|
50
59
|
): boolean => {
|
|
51
60
|
const values = allOptions || {};
|
|
52
|
-
if (message.when && !hasAllDependenciesFullfilled(message.when
|
|
61
|
+
if (message.when && !hasAllDependenciesFullfilled(message.when, values, optionsSchema)) {
|
|
53
62
|
return false;
|
|
54
63
|
}
|
|
55
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
message.unless &&
|
|
66
|
+
hasAllDependenciesFullfilled(message.unless, values, optionsSchema)
|
|
67
|
+
) {
|
|
56
68
|
return false;
|
|
57
69
|
}
|
|
58
70
|
return true;
|
|
@@ -65,12 +77,270 @@ export const getShownSchemaMessages = <T extends IConditionalFieldMessage>(
|
|
|
65
77
|
optionsSchema?: IQorusFormSchema
|
|
66
78
|
): T[] => (messages || []).filter((m) => isConditionalMessageShown(m, allOptions, optionsSchema));
|
|
67
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Name one `depends_on` entry so the reader can act on it.
|
|
82
|
+
*
|
|
83
|
+
* The list used to be built by looking each entry up as a whole field name,
|
|
84
|
+
* which is only ever true of the bare-name form. A COMPARISON entry
|
|
85
|
+
* (`kind=type`, `kind!=type`) matched no field, was dropped, and a field whose
|
|
86
|
+
* dependencies are all comparisons — the shape every subject field of a test
|
|
87
|
+
* uses — rendered "…are not fulfilled:" with nothing after the colon. The
|
|
88
|
+
* reader was told a dependency was unmet and not which one, by a sentence that
|
|
89
|
+
* stopped mid-way.
|
|
90
|
+
*
|
|
91
|
+
* One parser for both halves, the same one the evaluator uses, so the sentence
|
|
92
|
+
* and the lock cannot disagree about what an entry means.
|
|
93
|
+
*/
|
|
94
|
+
const describeDependency = (dependency: string, schema: IQorusFormSchema): string | undefined => {
|
|
95
|
+
const { name, op, value } = parseDependency(dependency);
|
|
96
|
+
const dependencySchema = schema[name];
|
|
97
|
+
|
|
98
|
+
if (!dependencySchema) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const label = `"${dependencySchema.display_name || name}"`;
|
|
103
|
+
|
|
104
|
+
// `!name` names no value to compare against — it is satisfied by the sibling
|
|
105
|
+
// being unanswered — so it gets a sentence of its own rather than the
|
|
106
|
+
// comparison template, which would read `must not be "undefined"`.
|
|
107
|
+
if (op === '!') {
|
|
108
|
+
return `${label} must have no value`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return op ? `${label} must ${op === '!=' ? 'not ' : ''}be "${value}"` : label;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The whole `depends_on` list as one phrase.
|
|
116
|
+
*
|
|
117
|
+
* A nested entry is an OR — only one of its members has to hold. It used to be
|
|
118
|
+
* flattened into the same comma-separated list as the top-level entries, which
|
|
119
|
+
* are an AND, so an either/or was shown to the reader as a set of things all
|
|
120
|
+
* required. The alternatives now stay grouped and joined by "or".
|
|
121
|
+
*/
|
|
122
|
+
export const describeDependencies = (
|
|
123
|
+
dependencies: (string | string[])[] | string[][],
|
|
124
|
+
schema: IQorusFormSchema
|
|
125
|
+
): string =>
|
|
126
|
+
(dependencies as (string | string[])[])
|
|
127
|
+
.map((entry) => {
|
|
128
|
+
if (!isArray(entry)) {
|
|
129
|
+
return describeDependency(entry as string, schema);
|
|
130
|
+
}
|
|
131
|
+
const alternatives = (entry as string[])
|
|
132
|
+
.map((dep) => describeDependency(dep, schema))
|
|
133
|
+
.filter((dep): dep is string => !!dep);
|
|
134
|
+
if (!alternatives.length) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return alternatives.length > 1 ? `(${alternatives.join(' or ')})` : alternatives[0];
|
|
138
|
+
})
|
|
139
|
+
.filter((entry): entry is string => !!entry)
|
|
140
|
+
.join(', ');
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* An allowed VALUE, as far as being offered is concerned.
|
|
144
|
+
*
|
|
145
|
+
* ts-toolkit 0.5.82 carries both `IQorusAllowedValue.depends_on` and
|
|
146
|
+
* `IQorusFormFieldMessage.when` / `unless`, so this no longer waits on a publish.
|
|
147
|
+
* It remains its own shape because it states only what DECIDING AVAILABILITY needs —
|
|
148
|
+
* three keys out of the twenty an allowed value can carry — so a caller that has
|
|
149
|
+
* only those three can be answered, and the predicate cannot quietly start reading
|
|
150
|
+
* a fourth.
|
|
151
|
+
*/
|
|
152
|
+
export interface IAllowedValueAvailabilityInput {
|
|
153
|
+
/**
|
|
154
|
+
* The value is offered only while every entry holds — the SAME grammar, the
|
|
155
|
+
* same parser and the same evaluator a field's `depends_on` uses, one level
|
|
156
|
+
* down. For what this form can decide on its own.
|
|
157
|
+
*/
|
|
158
|
+
depends_on?: (string | string[])[];
|
|
159
|
+
/**
|
|
160
|
+
* Someone who already knows has refused it — the server, for a sandbox or
|
|
161
|
+
* permission context, a capability of the selected kind, or a requirement in
|
|
162
|
+
* a scope `depends_on` deliberately cannot reach.
|
|
163
|
+
*/
|
|
164
|
+
disabled?: boolean;
|
|
165
|
+
/** Where a refusal says why. */
|
|
166
|
+
messages?: IConditionalFieldMessage[];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Whether a value can be picked, and the sentence that stands in for it. */
|
|
170
|
+
export interface IAllowedValueAvailability {
|
|
171
|
+
available: boolean;
|
|
172
|
+
/** Why not. Read by the row itself, by its tooltip and by the list's search. */
|
|
173
|
+
reason?: string;
|
|
174
|
+
/** A heading the refusal came with, when it came with one. */
|
|
175
|
+
title?: string;
|
|
176
|
+
intent?: TReqoreIntent;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** One object for every available value, so identity says "nothing to show". */
|
|
180
|
+
export const ALLOWED_VALUE_AVAILABLE: IAllowedValueAvailability = Object.freeze({
|
|
181
|
+
available: true,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Whether an offered value can be picked, and why not.
|
|
186
|
+
*
|
|
187
|
+
* Two things can refuse a value and they render identically, because a reader
|
|
188
|
+
* has no way to tell them apart and no reason to care:
|
|
189
|
+
*
|
|
190
|
+
* - **the predicate** — the value's own `depends_on`, judged against the form
|
|
191
|
+
* it is standing in. Whatever the form itself can decide.
|
|
192
|
+
* - **the pre-resolved refusal** — `disabled: true` with a message, from
|
|
193
|
+
* whoever already knows. A sandbox that denies a domain, a capability of the
|
|
194
|
+
* selected kind, a requirement in another value scope: none of them are
|
|
195
|
+
* reachable from a sibling's answer, and a predicate that pretended otherwise
|
|
196
|
+
* would be a predicate that never fires.
|
|
197
|
+
*
|
|
198
|
+
* The predicate is checked first when both could speak. It names a field in
|
|
199
|
+
* this very form, so the reader can act on it without leaving the row; a served
|
|
200
|
+
* reason is a statement about somewhere else.
|
|
201
|
+
*
|
|
202
|
+
* A value is never HIDDEN for either reason. A choice that vanishes takes its
|
|
203
|
+
* own explanation with it, and leaves the reader looking for something they
|
|
204
|
+
* were told exists.
|
|
205
|
+
*/
|
|
206
|
+
export const getAllowedValueAvailability = (
|
|
207
|
+
value: IAllowedValueAvailabilityInput | undefined,
|
|
208
|
+
allOptions?: TQorusForm,
|
|
209
|
+
schema?: IQorusFormSchema
|
|
210
|
+
): IAllowedValueAvailability => {
|
|
211
|
+
if (!value) {
|
|
212
|
+
return ALLOWED_VALUE_AVAILABLE;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* `allOptions` is undefined only when there is no form around the picker at
|
|
216
|
+
all (see `OptionsContext`). A predicate about siblings has nothing to judge
|
|
217
|
+
there, and locking every gated value in a standalone picker would be a
|
|
218
|
+
rendering fault rather than a fact about the form. */
|
|
219
|
+
if (
|
|
220
|
+
size(value.depends_on) &&
|
|
221
|
+
allOptions &&
|
|
222
|
+
!hasAllDependenciesFullfilled(value.depends_on!, allOptions, schema)
|
|
223
|
+
) {
|
|
224
|
+
const dependsOn = schema ? describeDependencies(value.depends_on!, schema) : '';
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
available: false,
|
|
228
|
+
intent: 'warning',
|
|
229
|
+
reason:
|
|
230
|
+
dependsOn ?
|
|
231
|
+
`Unavailable because some dependencies are not fulfilled: ${dependsOn}`
|
|
232
|
+
// Nothing nameable: every entry pointed at a field this schema does
|
|
233
|
+
// not contain. A sentence that ends in a colon and then stops reads
|
|
234
|
+
// as a rendering fault rather than as a fact about the form.
|
|
235
|
+
: 'Unavailable because some dependencies are not fulfilled',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (value.disabled) {
|
|
240
|
+
/* The refusal picks its own words out of `messages`, through the same
|
|
241
|
+
`when`/`unless` filter a field's messages go through — a served reason
|
|
242
|
+
can itself be conditional. The most serious one speaks: a `danger`
|
|
243
|
+
message is the one that explains a refusal, and an `info` note beside it
|
|
244
|
+
is not. */
|
|
245
|
+
const explanation = getRefusalMessage(getShownSchemaMessages(value.messages, allOptions, schema));
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
available: false,
|
|
249
|
+
intent: (explanation?.intent as TReqoreIntent) || 'warning',
|
|
250
|
+
title: explanation?.title,
|
|
251
|
+
// A refusal with no message at all still has to say something: silence
|
|
252
|
+
// over a value that will not respond is the defect this whole mechanism
|
|
253
|
+
// exists to remove.
|
|
254
|
+
reason: explanation?.content || UNAVAILABLE_VALUE_FALLBACK_REASON,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return ALLOWED_VALUE_AVAILABLE;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The availability of each offered value, against the form the picker stands in.
|
|
263
|
+
*
|
|
264
|
+
* Aligned with `items`: the result at index `i` describes `items[i]`.
|
|
265
|
+
*
|
|
266
|
+
* The answer is computed INSIDE the context selector, not from a copy of the
|
|
267
|
+
* form values pulled out of it. `OptionsContext` hands down a fresh object on
|
|
268
|
+
* every render of the engine, so a picker that selected the values themselves
|
|
269
|
+
* would re-render on every keystroke anywhere in the form; selecting the
|
|
270
|
+
* computed answer re-renders it only when an availability actually changes.
|
|
271
|
+
*
|
|
272
|
+
* Values that gate on nothing skip the computation entirely, and that is a
|
|
273
|
+
* claim about each VALUE rather than about the field it belongs to. The gate
|
|
274
|
+
* used to be one flag for the whole list, so a single gated value in a
|
|
275
|
+
* thousand-item picker resolved all thousand and serialised all thousand on
|
|
276
|
+
* every keystroke anywhere in the form. Only the gated indices are resolved
|
|
277
|
+
* and serialised; every other position is the same frozen
|
|
278
|
+
* {@link ALLOWED_VALUE_AVAILABLE}, which costs nothing to fill in and lets
|
|
279
|
+
* identity say "nothing to show".
|
|
280
|
+
*/
|
|
281
|
+
export const useAllowedValueAvailability = (
|
|
282
|
+
items: readonly IAllowedValueAvailabilityInput[] | undefined
|
|
283
|
+
): IAllowedValueAvailability[] => {
|
|
284
|
+
const gatedIndices = useMemo(
|
|
285
|
+
() =>
|
|
286
|
+
(items || []).reduce<number[]>((indices, item, index) => {
|
|
287
|
+
if (size(item?.depends_on) || item?.disabled === true) {
|
|
288
|
+
indices.push(index);
|
|
289
|
+
}
|
|
290
|
+
return indices;
|
|
291
|
+
}, []),
|
|
292
|
+
[items]
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const serialized = useContextSelector(OptionsContext, (context) =>
|
|
296
|
+
gatedIndices.length ?
|
|
297
|
+
JSON.stringify(
|
|
298
|
+
gatedIndices.map((index) =>
|
|
299
|
+
getAllowedValueAvailability(items![index], context.value, context.schema)
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
: ''
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return useMemo(() => {
|
|
306
|
+
const result = (items || []).map(() => ALLOWED_VALUE_AVAILABLE);
|
|
307
|
+
|
|
308
|
+
if (!serialized) {
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Aligned back onto the caller's own indices: the picker asks for
|
|
313
|
+
// `availability[i]` and must get the answer for `items[i]`.
|
|
314
|
+
const resolved = JSON.parse(serialized) as IAllowedValueAvailability[];
|
|
315
|
+
gatedIndices.forEach((index, position) => {
|
|
316
|
+
result[index] = resolved[position];
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
return result;
|
|
320
|
+
}, [serialized, items, gatedIndices]);
|
|
321
|
+
};
|
|
322
|
+
|
|
68
323
|
export interface IOptionFieldMessagesProps {
|
|
69
324
|
schema: IQorusFormSchema;
|
|
70
325
|
option: IQorusFormField;
|
|
71
326
|
allOptions?: TQorusForm;
|
|
72
327
|
name: string;
|
|
73
328
|
getType: (type: string) => string;
|
|
329
|
+
/**
|
|
330
|
+
* The reader has not edited this field in this session.
|
|
331
|
+
*
|
|
332
|
+
* An error is a report that something went WRONG. "This field is required"
|
|
333
|
+
* under a field nobody has been in yet reports nothing: the form is empty
|
|
334
|
+
* because it is new. The requirement is already carried three other ways —
|
|
335
|
+
* the asterisk on the label, the Needs-attention box the row sits in, and the
|
|
336
|
+
* completion meter — so the message waits until the reader has been in the
|
|
337
|
+
* field and left it empty, which IS a thing that went wrong.
|
|
338
|
+
*
|
|
339
|
+
* Only the plain required message is held back. A value that fails
|
|
340
|
+
* validation, an unmet required GROUP and a locked dependency are all facts
|
|
341
|
+
* about what the form currently holds, and they show immediately.
|
|
342
|
+
*/
|
|
343
|
+
untouched?: boolean;
|
|
74
344
|
}
|
|
75
345
|
|
|
76
346
|
/**
|
|
@@ -84,13 +354,26 @@ export const getOptionFieldMessages = ({
|
|
|
84
354
|
name,
|
|
85
355
|
allOptions,
|
|
86
356
|
getType,
|
|
357
|
+
untouched,
|
|
87
358
|
}: IOptionFieldMessagesProps): IReqoreTagProps[] => {
|
|
88
359
|
const optionSchema = schema[name];
|
|
89
360
|
const result: IReqoreTagProps[] = [];
|
|
90
361
|
|
|
91
362
|
if (option.value || option.value === false || option.value === 0 || option.value === null) {
|
|
363
|
+
/* A concrete type the VALUE carries beats an untyped schema.
|
|
364
|
+
`auto`/`any` declare no type — the author picks one (Binary, Date, …)
|
|
365
|
+
and it is recorded beside the value. Reading the schema first validated
|
|
366
|
+
a binary value as `auto`, which auto-detects from the value and accepts
|
|
367
|
+
anything, so the form said the field needed attention while the field
|
|
368
|
+
itself gave no reason. The form's own check already prefers the stored
|
|
369
|
+
type (`getOptionFieldStorageType`); this is the same precedence. */
|
|
370
|
+
const schemaType = optionSchema?.ui_type as string;
|
|
371
|
+
const storedType = option.type as string;
|
|
372
|
+
const typeToValidate =
|
|
373
|
+
isUntypedOptionType(schemaType) && storedType ? storedType : schemaType || storedType;
|
|
374
|
+
|
|
92
375
|
const validationData = validateFieldWithResult(
|
|
93
|
-
getType(
|
|
376
|
+
getType(typeToValidate),
|
|
94
377
|
option.value,
|
|
95
378
|
{
|
|
96
379
|
has_to_have_value: true,
|
|
@@ -109,7 +392,7 @@ export const getOptionFieldMessages = ({
|
|
|
109
392
|
});
|
|
110
393
|
}
|
|
111
394
|
} else {
|
|
112
|
-
if (optionSchema?.required) {
|
|
395
|
+
if (optionSchema?.required && !untouched) {
|
|
113
396
|
result.push({ label: 'This field is required', intent: 'danger' });
|
|
114
397
|
}
|
|
115
398
|
|
|
@@ -134,14 +417,16 @@ export const getOptionFieldMessages = ({
|
|
|
134
417
|
optionSchema?.depends_on &&
|
|
135
418
|
!hasAllDependenciesFullfilled(optionSchema.depends_on, allOptions, schema)
|
|
136
419
|
) {
|
|
137
|
-
const dependsOn =
|
|
138
|
-
.filter((dep: string) => !!schema[dep])
|
|
139
|
-
.map((dep: string) => schema[dep].display_name || dep)
|
|
140
|
-
.map((dep: string) => `"${dep}"`)
|
|
141
|
-
.join(', ');
|
|
420
|
+
const dependsOn = describeDependencies(optionSchema.depends_on, schema);
|
|
142
421
|
|
|
143
422
|
result.push({
|
|
144
|
-
label:
|
|
423
|
+
label:
|
|
424
|
+
dependsOn ?
|
|
425
|
+
`This field is disabled because some dependencies are not fulfilled: ${dependsOn}`
|
|
426
|
+
// Nothing nameable: every entry pointed at a field this schema does
|
|
427
|
+
// not contain. A sentence that ends in a colon and then stops reads
|
|
428
|
+
// as a rendering fault rather than as a fact about the form.
|
|
429
|
+
: 'This field is disabled because some dependencies are not fulfilled',
|
|
145
430
|
intent: 'warning',
|
|
146
431
|
});
|
|
147
432
|
}
|
|
@@ -155,10 +440,11 @@ export const OptionFieldMessages = ({
|
|
|
155
440
|
name,
|
|
156
441
|
allOptions,
|
|
157
442
|
getType,
|
|
443
|
+
untouched,
|
|
158
444
|
}: IOptionFieldMessagesProps) => {
|
|
159
445
|
const messages: IReqoreTagProps[] = useMemo(
|
|
160
|
-
() => getOptionFieldMessages({ schema, option, name, allOptions, getType }),
|
|
161
|
-
[JSON.stringify(schema), JSON.stringify(option), JSON.stringify(allOptions), name]
|
|
446
|
+
() => getOptionFieldMessages({ schema, option, name, allOptions, getType, untouched }),
|
|
447
|
+
[JSON.stringify(schema), JSON.stringify(option), JSON.stringify(allOptions), name, untouched]
|
|
162
448
|
);
|
|
163
449
|
|
|
164
450
|
if (!size(messages)) {
|
|
@@ -5,11 +5,14 @@ import {
|
|
|
5
5
|
ReqoreP,
|
|
6
6
|
ReqoreTag,
|
|
7
7
|
} from '@qoretechnologies/reqore';
|
|
8
|
+
import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
8
9
|
import {
|
|
9
10
|
IQorusFormField,
|
|
10
11
|
IQorusFormSchema,
|
|
11
12
|
TQorusFormFieldSchema,
|
|
12
13
|
} from '@qoretechnologies/ts-toolkit';
|
|
14
|
+
import { IRichtextSegment } from '../../../../helpers/common';
|
|
15
|
+
import { templateTextSegments } from '../../../../helpers/templateText';
|
|
13
16
|
import React from 'react';
|
|
14
17
|
import styled from 'styled-components';
|
|
15
18
|
import { useContextSelector } from 'use-context-selector';
|
|
@@ -24,12 +27,15 @@ import {
|
|
|
24
27
|
fieldLabel,
|
|
25
28
|
findAllowedValueOption,
|
|
26
29
|
formatOptionValue,
|
|
30
|
+
getAllowedValueIcon,
|
|
27
31
|
getAllowedValueImage,
|
|
28
32
|
isCodeField,
|
|
29
33
|
orderedKeys,
|
|
30
34
|
recordIdentity,
|
|
31
35
|
titleKeyFor,
|
|
32
36
|
} from '../readFirst';
|
|
37
|
+
import { TemplateText } from '../../fields/template/TemplateText';
|
|
38
|
+
import { TFieldWithOwnTemplates } from '../rendererTypes';
|
|
33
39
|
import { isEmptyUiEnvelope, isUiEncodedValue } from './structuredData';
|
|
34
40
|
|
|
35
41
|
/**
|
|
@@ -343,6 +349,32 @@ const labelColumn = (value: unknown, schema: IQorusFormSchema): string => {
|
|
|
343
349
|
const isNavigableUrl = (value: unknown): value is string =>
|
|
344
350
|
typeof value === 'string' && /^(https?|wss?|ftps?):\/\//i.test(value);
|
|
345
351
|
|
|
352
|
+
/**
|
|
353
|
+
* The reference chips inside a formatted value — none when it holds no
|
|
354
|
+
* reference at all.
|
|
355
|
+
*
|
|
356
|
+
* The preview is the SAME value the row above draws, one level down, and it
|
|
357
|
+
* reached the mono cell as a raw string: a sub-field holding
|
|
358
|
+
* `$._case.mode != 'simulate'` read as a chip on the row and as the engine's
|
|
359
|
+
* own spelling in the inset beneath it, which is a row disagreeing with itself
|
|
360
|
+
* about what its value is.
|
|
361
|
+
*
|
|
362
|
+
* A field's OWN list and grammar win where it declares them — the same
|
|
363
|
+
* precedence the rows use — falling back to the form's shared catalogue.
|
|
364
|
+
*/
|
|
365
|
+
const referenceSegments = (
|
|
366
|
+
text: unknown,
|
|
367
|
+
templates: IReqoreFormTemplates | undefined,
|
|
368
|
+
fieldSchema?: TQorusFormFieldSchema
|
|
369
|
+
): IRichtextSegment[] => {
|
|
370
|
+
if (typeof text !== 'string' || !text) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
const own = fieldSchema as TFieldWithOwnTemplates | undefined;
|
|
374
|
+
const segments = templateTextSegments(text, own?.templates ?? templates, own?.templateToken);
|
|
375
|
+
return segments.some((segment) => segment.kind === 'tag') ? segments : [];
|
|
376
|
+
};
|
|
377
|
+
|
|
346
378
|
/** One field's value, rendered the way its row above renders it. */
|
|
347
379
|
const FieldValue = ({
|
|
348
380
|
field,
|
|
@@ -356,7 +388,11 @@ const FieldValue = ({
|
|
|
356
388
|
const formatted = formatOptionValue(field, fieldSchema);
|
|
357
389
|
// An allowed value that carries a logo shows it here too, exactly as the row
|
|
358
390
|
// above does — the preview is the same value, so it gets the same treatment.
|
|
391
|
+
// An option marked with an ICON rather than a logo gets the same, for the same
|
|
392
|
+
// reason: the mark is how a list of bare words is told apart, and dropping it
|
|
393
|
+
// here would make the preview disagree with the row it previews.
|
|
359
394
|
const image = getAllowedValueImage(field.value, fieldSchema);
|
|
395
|
+
const icon = getAllowedValueIcon(field.value, fieldSchema);
|
|
360
396
|
|
|
361
397
|
// Data reads in mono; a chosen label does not.
|
|
362
398
|
//
|
|
@@ -368,6 +404,13 @@ const FieldValue = ({
|
|
|
368
404
|
// separates the two, and it is the same lookup that produced the text.
|
|
369
405
|
const isChosenLabel = !!findAllowedValueOption(field.value, fieldSchema);
|
|
370
406
|
|
|
407
|
+
/* Read from context, like the code renderer above: a level is arbitrarily
|
|
408
|
+
deep, and the form's catalogue is a property of the form rather than of any
|
|
409
|
+
one level. See `referenceSegments`. */
|
|
410
|
+
const templates = useContextSelector(CompactRowContext, (v) => v.templates);
|
|
411
|
+
const fieldTemplates = (fieldSchema as TFieldWithOwnTemplates | undefined)?.templates ?? templates;
|
|
412
|
+
const segments = isNavigableUrl(field.value) ? [] : referenceSegments(formatted, templates, fieldSchema);
|
|
413
|
+
|
|
371
414
|
return (
|
|
372
415
|
<StyledRowValue $color={color}>
|
|
373
416
|
{field.value === true || field.value === false ?
|
|
@@ -387,6 +430,12 @@ const FieldValue = ({
|
|
|
387
430
|
size='14px'
|
|
388
431
|
style={{ flexShrink: 0, marginRight: 6, verticalAlign: 'middle' }}
|
|
389
432
|
/>
|
|
433
|
+
: icon ?
|
|
434
|
+
<ReqoreIcon
|
|
435
|
+
icon={icon as any}
|
|
436
|
+
size='14px'
|
|
437
|
+
style={{ flexShrink: 0, marginRight: 6, verticalAlign: 'middle' }}
|
|
438
|
+
/>
|
|
390
439
|
: null}
|
|
391
440
|
{isNavigableUrl(field.value) ?
|
|
392
441
|
// The address IS the link — it used to render as inert text with a
|
|
@@ -403,6 +452,10 @@ const FieldValue = ({
|
|
|
403
452
|
>
|
|
404
453
|
{formatted}
|
|
405
454
|
</ReqoreLink>
|
|
455
|
+
: segments.length ?
|
|
456
|
+
// `full`: the preview is where a value that the row had to cut is
|
|
457
|
+
// shown whole, so its chips wrap rather than clipping.
|
|
458
|
+
<TemplateText segments={segments} templates={fieldTemplates} full />
|
|
406
459
|
: formatted}
|
|
407
460
|
</span>
|
|
408
461
|
}
|
|
@@ -410,6 +463,25 @@ const FieldValue = ({
|
|
|
410
463
|
);
|
|
411
464
|
};
|
|
412
465
|
|
|
466
|
+
/**
|
|
467
|
+
* A value with no field schema of its own — a bare entry in a list, a level
|
|
468
|
+
* that turned out to be a scalar. It still reads as prose with named chips
|
|
469
|
+
* where it holds references, because a reference is not more raw for having
|
|
470
|
+
* arrived without a schema.
|
|
471
|
+
*/
|
|
472
|
+
const ScalarText = ({
|
|
473
|
+
text,
|
|
474
|
+
templates,
|
|
475
|
+
}: {
|
|
476
|
+
text: string;
|
|
477
|
+
templates: IReqoreFormTemplates | undefined;
|
|
478
|
+
}) => {
|
|
479
|
+
const segments = referenceSegments(text, templates);
|
|
480
|
+
return segments.length ?
|
|
481
|
+
<TemplateText segments={segments} templates={templates} full />
|
|
482
|
+
: <>{text}</>;
|
|
483
|
+
};
|
|
484
|
+
|
|
413
485
|
/** One described hash: its fields as label/value pairs in the shared columns. */
|
|
414
486
|
const SchemaRecord = ({
|
|
415
487
|
record,
|
|
@@ -547,6 +619,9 @@ const SchemaLevel = ({
|
|
|
547
619
|
colors: ISchemaDataViewProps['colors'];
|
|
548
620
|
}) => {
|
|
549
621
|
const inner = unwrap(value);
|
|
622
|
+
// Same reason as `FieldValue`: a heading and a bare list entry are values too,
|
|
623
|
+
// and a reference in one reads as a name everywhere else in the form.
|
|
624
|
+
const templates = useContextSelector(CompactRowContext, (v) => v.templates);
|
|
550
625
|
|
|
551
626
|
if (Array.isArray(inner)) {
|
|
552
627
|
return (
|
|
@@ -567,6 +642,14 @@ const SchemaLevel = ({
|
|
|
567
642
|
if (!identity) {
|
|
568
643
|
return null;
|
|
569
644
|
}
|
|
645
|
+
// An item is headed by one of its own fields, so the
|
|
646
|
+
// heading is that field's value and reads like it: a case
|
|
647
|
+
// named by a reference is named by the reference's NAME.
|
|
648
|
+
const titleSegments = referenceSegments(
|
|
649
|
+
identity.text,
|
|
650
|
+
templates,
|
|
651
|
+
schema[identity.key] as TQorusFormFieldSchema | undefined
|
|
652
|
+
);
|
|
570
653
|
return (
|
|
571
654
|
<StyledItemTitle
|
|
572
655
|
className='schema-view-item-title'
|
|
@@ -578,7 +661,9 @@ const SchemaLevel = ({
|
|
|
578
661
|
$accent={colors.accent}
|
|
579
662
|
title={identity.label}
|
|
580
663
|
>
|
|
581
|
-
{
|
|
664
|
+
{titleSegments.length ?
|
|
665
|
+
<TemplateText segments={titleSegments} templates={templates} full />
|
|
666
|
+
: identity.text}
|
|
582
667
|
</StyledItemTitle>
|
|
583
668
|
);
|
|
584
669
|
})()}
|
|
@@ -595,7 +680,10 @@ const SchemaLevel = ({
|
|
|
595
680
|
className='options-readfirst-valuetext schema-view-data'
|
|
596
681
|
style={{ fontFamily: MONO_FONT_STACK }}
|
|
597
682
|
>
|
|
598
|
-
|
|
683
|
+
<ScalarText
|
|
684
|
+
text={formatOptionValue({ value: record } as IQorusFormField)}
|
|
685
|
+
templates={templates}
|
|
686
|
+
/>
|
|
599
687
|
</span>
|
|
600
688
|
</StyledRowValue>
|
|
601
689
|
}
|
|
@@ -613,7 +701,10 @@ const SchemaLevel = ({
|
|
|
613
701
|
|
|
614
702
|
return (
|
|
615
703
|
<ReqoreP size='small' style={{ color: colors.key }}>
|
|
616
|
-
|
|
704
|
+
<ScalarText
|
|
705
|
+
text={formatOptionValue({ value: inner } as IQorusFormField)}
|
|
706
|
+
templates={templates}
|
|
707
|
+
/>
|
|
617
708
|
</ReqoreP>
|
|
618
709
|
);
|
|
619
710
|
};
|
|
@@ -39,6 +39,21 @@ export type TCodePreviewRenderer = (props: {
|
|
|
39
39
|
export interface ICompactRowContext {
|
|
40
40
|
// Props / config
|
|
41
41
|
readOnly?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* What a click on a READ-ONLY row should do, when the consumer has somewhere
|
|
44
|
+
* to send it.
|
|
45
|
+
*
|
|
46
|
+
* A read-only row is not a control, so by default it opens nothing: it shows
|
|
47
|
+
* everything it has and answers a click with silence. That is right when
|
|
48
|
+
* there is nowhere else to go, and a dead end when there is — a reader
|
|
49
|
+
* clicking a value they want to change is telling you exactly what they want,
|
|
50
|
+
* and being ignored teaches them the surface is broken.
|
|
51
|
+
*
|
|
52
|
+
* Given this, the row becomes the way in to wherever that field IS editable,
|
|
53
|
+
* and says so: it takes a button's role and keyboard handling. Without it
|
|
54
|
+
* nothing changes, so a read-only form with no editor behind it stays inert.
|
|
55
|
+
*/
|
|
56
|
+
onReadOnlyActivate?: (optionName: string) => void;
|
|
42
57
|
commitMode: 'immediate' | 'batched';
|
|
43
58
|
expandMode: 'single' | 'multi';
|
|
44
59
|
|
|
@@ -54,12 +69,6 @@ export interface ICompactRowContext {
|
|
|
54
69
|
// display name in the read-first chip.
|
|
55
70
|
templates?: IReqoreFormTemplates;
|
|
56
71
|
expandedOptions: string[];
|
|
57
|
-
// Name of the field the engine expanded programmatically for
|
|
58
|
-
// `autoFocusFirstRequired`. CompactRow focuses this one with `preventScroll`
|
|
59
|
-
// so an off-screen / below-the-fold form is never scrolled into view on
|
|
60
|
-
// mount. A ref (not state) so it's current when the 60ms focus timer fires,
|
|
61
|
-
// regardless of render batching.
|
|
62
|
-
autoFocusNameRef?: MutableRefObject<string | undefined>;
|
|
63
72
|
highlightedOptions: string[];
|
|
64
73
|
flashedOptions: string[];
|
|
65
74
|
infoPanelOverrides: Record<string, boolean>;
|