@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
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
ReqoreTagGroup,
|
|
11
11
|
} from '@qoretechnologies/reqore';
|
|
12
12
|
import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
13
|
+
import { RowMenuContext, useRowMenuRegistry } from './rowMenuContext';
|
|
13
14
|
import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
|
|
14
|
-
import { resolveOptionActions } from './optionActions';
|
|
15
|
+
import { optionActionAsMenuItem, resolveOptionActions, splitOptionActions } from './optionActions';
|
|
15
16
|
import {
|
|
16
17
|
IQorusFormField,
|
|
17
18
|
IQorusFormSchema,
|
|
@@ -24,16 +25,11 @@ import size from 'lodash/size';
|
|
|
24
25
|
import React, { memo } from 'react';
|
|
25
26
|
import { useContextSelector } from 'use-context-selector';
|
|
26
27
|
import { hasAllDependenciesFullfilled, parseDependency } from '../../../helpers/validations';
|
|
27
|
-
import {
|
|
28
|
-
findTemplate,
|
|
29
|
-
getTemplateTagStyle,
|
|
30
|
-
isValueTemplate,
|
|
31
|
-
describeTemplateReference,
|
|
32
|
-
splitTemplateTokens,
|
|
33
|
-
TTemplateMeta,
|
|
34
|
-
} from '../../../helpers/templates';
|
|
28
|
+
import { findTemplate, isValueTemplate } from '../../../helpers/templates';
|
|
35
29
|
import { getDefaultValue, richtextToSegments, richtextToString } from '../../../helpers/common';
|
|
30
|
+
import { hasTemplateText, templateTextSegments } from '../../../helpers/templateText';
|
|
36
31
|
import { ReadOnlyTemplateTag } from '../fields/template/ReadOnlyTemplateTag';
|
|
32
|
+
import { TemplateText } from '../fields/template/TemplateText';
|
|
37
33
|
import { describeCodeSize, formatCodeChars, formatCodeLines } from '../../codeSize';
|
|
38
34
|
import { Description } from '../../Description';
|
|
39
35
|
import { useMarkdownRenderer } from '../../Description/markdownRendererContext';
|
|
@@ -57,6 +53,8 @@ import {
|
|
|
57
53
|
StyledStatusDot,
|
|
58
54
|
} from './compactRowStyles';
|
|
59
55
|
import { getShownSchemaMessages, getOptionFieldMessages } from './OptionFieldMessages';
|
|
56
|
+
import { findRowFocusTarget, isClosedPickerTrigger } from './rowFocus';
|
|
57
|
+
import { RowOpenPickerContext } from './rowOpenPicker';
|
|
60
58
|
import { SchemaDataView, canRenderWithSchema } from './_structuredData/SchemaDataView';
|
|
61
59
|
import { query } from '../../../utils/fetch';
|
|
62
60
|
import {
|
|
@@ -64,6 +62,7 @@ import {
|
|
|
64
62
|
findAllowedValueOption,
|
|
65
63
|
formatBytes,
|
|
66
64
|
formatOptionValue,
|
|
65
|
+
getAllowedValueIcon,
|
|
67
66
|
getAllowedValueImage,
|
|
68
67
|
getFileSize,
|
|
69
68
|
getHashEntries,
|
|
@@ -72,8 +71,11 @@ import {
|
|
|
72
71
|
isFixedCompactAllowedValueOption,
|
|
73
72
|
isOptionValueEmpty,
|
|
74
73
|
optionHasImages,
|
|
74
|
+
getExpressionAst,
|
|
75
75
|
} from './readFirst';
|
|
76
76
|
import { StructuredDataView } from './_structuredData/StructuredDataView';
|
|
77
|
+
import { DpqlRendering } from '../expressions/DpqlRendering';
|
|
78
|
+
import { TFieldWithOwnTemplates } from './rendererTypes';
|
|
77
79
|
|
|
78
80
|
/**
|
|
79
81
|
* Does this markdown value have more document in it than one row can show?
|
|
@@ -131,12 +133,6 @@ const COMPACT_SINGLE_VALUE_TYPES = new Set([
|
|
|
131
133
|
'method-name',
|
|
132
134
|
]);
|
|
133
135
|
|
|
134
|
-
// How many consumer-injected actions may sit inline in a row before the rest
|
|
135
|
-
// overflow into the row's menu. A row's action slot shares space with the
|
|
136
|
-
// value; an unbounded button strip would squeeze the value out, and the
|
|
137
|
-
// consumer controls how many actions it injects.
|
|
138
|
-
const MAX_INLINE_OPTION_ACTIONS = 2;
|
|
139
|
-
|
|
140
136
|
// One read-first row: label | value | action collapsed; the real editor (the
|
|
141
137
|
// classic renderOption) expanded. `hidden` = search-surfaced optional —
|
|
142
138
|
// activating the row adds the field first.
|
|
@@ -202,7 +198,6 @@ export const CompactRow = memo(
|
|
|
202
198
|
const isExpanded = useContextSelector(CompactRowContext, (v) =>
|
|
203
199
|
v.expandedOptions.includes(optionName)
|
|
204
200
|
);
|
|
205
|
-
const autoFocusNameRef = useContextSelector(CompactRowContext, (v) => v.autoFocusNameRef);
|
|
206
201
|
const isHighlighted = useContextSelector(CompactRowContext, (v) =>
|
|
207
202
|
v.highlightedOptions.includes(optionName)
|
|
208
203
|
);
|
|
@@ -215,6 +210,44 @@ export const CompactRow = memo(
|
|
|
215
210
|
);
|
|
216
211
|
const setFocusedEditing = useContextSelector(CompactRowContext, (v) => v.setFocusedEditing);
|
|
217
212
|
const readRowHeights = useContextSelector(CompactRowContext, (v) => v.readRowHeights);
|
|
213
|
+
const onReadOnlyActivate = useContextSelector(
|
|
214
|
+
CompactRowContext,
|
|
215
|
+
(v) => v.onReadOnlyActivate
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The floor recorded when a row is opened lasts until the author changes
|
|
220
|
+
* something.
|
|
221
|
+
*
|
|
222
|
+
* `activate()` measures the row as it was READ and pins that as the
|
|
223
|
+
* editing row's `min-height`, so opening a row cannot yank the content
|
|
224
|
+
* below it up under the pointer. That is right for the transition and
|
|
225
|
+
* wrong for the rest of the session: a row that was tall because it
|
|
226
|
+
* carried a warning keeps that height after the author fixes the very
|
|
227
|
+
* thing the warning was about, leaving a large empty box where the
|
|
228
|
+
* explanation used to be — reported as "the chip is removed, but the
|
|
229
|
+
* vertical size of the option remains the same".
|
|
230
|
+
*
|
|
231
|
+
* Released on the first change to the value, which is exactly the moment
|
|
232
|
+
* the author has acted and the row should settle to what it now holds.
|
|
233
|
+
* Nothing is yanked by that: the author caused it and is looking at it.
|
|
234
|
+
*
|
|
235
|
+
* Keyed on a STRING rather than the value itself. A form value is often a
|
|
236
|
+
* fresh object on every render (`{source, value}` for a reference), so a
|
|
237
|
+
* dependency on its identity would drop the floor on the first re-render
|
|
238
|
+
* and the transition it exists for would never be smoothed at all.
|
|
239
|
+
*/
|
|
240
|
+
const valueKey =
|
|
241
|
+
optionField?.value === null || typeof optionField?.value !== 'object' ?
|
|
242
|
+
String(optionField?.value)
|
|
243
|
+
: JSON.stringify(optionField?.value);
|
|
244
|
+
|
|
245
|
+
/* `valueKey` alone, deliberately - see above. No eslint-disable: this repo
|
|
246
|
+
does not load `react-hooks`, so a suppression for a rule that is not
|
|
247
|
+
there is itself an error, and the pre-push lint refuses the push. */
|
|
248
|
+
React.useEffect(() => {
|
|
249
|
+
delete readRowHeights.current[optionName];
|
|
250
|
+
}, [valueKey]);
|
|
218
251
|
const originalValue = useContextSelector(CompactRowContext, (v) => v.originalValue);
|
|
219
252
|
const availableOptions = useContextSelector(CompactRowContext, (v) => v.availableOptions);
|
|
220
253
|
const requiredGroupsInfo = useContextSelector(CompactRowContext, (v) => v.requiredGroupsInfo);
|
|
@@ -255,27 +288,6 @@ export const CompactRow = memo(
|
|
|
255
288
|
|
|
256
289
|
// Value-cell content: colour adds a swatch, file an icon + size; hash keeps
|
|
257
290
|
// its "N fields" summary (sub-fields reveal beneath the row).
|
|
258
|
-
/**
|
|
259
|
-
* Flattens the line breaks in one prose segment of a richtext SUMMARY.
|
|
260
|
-
*
|
|
261
|
-
* The read-first row is a single line: every other value type reaches it
|
|
262
|
-
* through `whiteSpace: 'nowrap'`, which collapses newlines for free. The
|
|
263
|
-
* richtext branch is the only one that opts into `'pre'` — it has to, or the
|
|
264
|
-
* spaces that separate a word from the chip beside it are dropped — and `pre`
|
|
265
|
-
* also honours the newlines, which `nowrap` would have eaten.
|
|
266
|
-
*
|
|
267
|
-
* So a genuinely multi-line value (an alert rule's Gmail message body is five
|
|
268
|
-
* `\n`-separated lines) gave each segment after the first a blank first line.
|
|
269
|
-
* The wrapper centres its items, so a two-line-tall box centred against
|
|
270
|
-
* one-line chips put every word 12px below the chip beside it, and the row
|
|
271
|
-
* read as a staircase. Measured on supah: prose boxes 30px against 14px chips.
|
|
272
|
-
*
|
|
273
|
-
* A space, not nothing: consecutive prose segments are merged before they get
|
|
274
|
-
* here, so a value with a break and no chip between its lines would otherwise
|
|
275
|
-
* lose the word boundary entirely.
|
|
276
|
-
*/
|
|
277
|
-
const collapseSummaryBreaks = (text: string): string => text.replace(/\s*\r?\n\s*/g, ' ');
|
|
278
|
-
|
|
279
291
|
const renderReadFirstValue = (
|
|
280
292
|
field: IQorusFormField,
|
|
281
293
|
schema: TQorusFormFieldSchema | undefined,
|
|
@@ -299,6 +311,43 @@ export const CompactRow = memo(
|
|
|
299
311
|
{ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }
|
|
300
312
|
: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
301
313
|
|
|
314
|
+
/* A row has to SAY when its value is an expression.
|
|
315
|
+
|
|
316
|
+
Reported from the live IDE: an author accepted the *Use as expression*
|
|
317
|
+
offer on one field and not on the other, and the two rows read exactly
|
|
318
|
+
the same - `1 + 2` and `2 + 1`, both plain text - so there was no way
|
|
319
|
+
to tell which had been converted. The assertion then compared the
|
|
320
|
+
string `"1 + 2"` against the number `3` and the author had no way to
|
|
321
|
+
see why.
|
|
322
|
+
|
|
323
|
+
The same Σ the expression editor uses, so the mark means the same thing
|
|
324
|
+
wherever it appears. */
|
|
325
|
+
const withExpressionMarker = (rendered: React.ReactNode): React.ReactNode => {
|
|
326
|
+
if (!(field as { is_expression?: boolean })?.is_expression) {
|
|
327
|
+
return rendered;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<span style={{ ...wrapStyle, gap: 4 }}>
|
|
332
|
+
<ReqoreIcon
|
|
333
|
+
icon='Functions'
|
|
334
|
+
size='tiny'
|
|
335
|
+
/* A stable hook: reqore renders every icon as a bare
|
|
336
|
+
`.reqore-icon`, so without this there is nothing to select the
|
|
337
|
+
mark by — in a test or in a consumer's stylesheet. */
|
|
338
|
+
className='reqraft-expression-marker'
|
|
339
|
+
/* Opacity through `effect`, which is how this repo dims a reqore
|
|
340
|
+
component (see the card's `short_desc` below); only the flex
|
|
341
|
+
rule, which reqore has no prop for, stays inline. */
|
|
342
|
+
effect={{ opacity: 0.7 }}
|
|
343
|
+
style={{ flexShrink: 0 }}
|
|
344
|
+
tooltip='This value is an expression'
|
|
345
|
+
/>
|
|
346
|
+
{rendered}
|
|
347
|
+
</span>
|
|
348
|
+
);
|
|
349
|
+
};
|
|
350
|
+
|
|
302
351
|
if (valueType === 'rgbcolor') {
|
|
303
352
|
const swatch = colorToCss(field?.value);
|
|
304
353
|
return (
|
|
@@ -351,6 +400,52 @@ export const CompactRow = memo(
|
|
|
351
400
|
);
|
|
352
401
|
}
|
|
353
402
|
|
|
403
|
+
// A test's cases read as CHIPS here, the same as the names in the table
|
|
404
|
+
// this row opens (`CasesTable`, qorus-ide). `test-cases` is a
|
|
405
|
+
// renderer-only ui_type, so it reached none of the branches above and
|
|
406
|
+
// fell through to the generic summary, which flattens the array of case
|
|
407
|
+
// hashes to a comma-joined string — "case_1, resolved_expected_values".
|
|
408
|
+
// A case name is an identifier the author types into `$.` references and
|
|
409
|
+
// skip conditions, and a chip is how the rest of the product writes an
|
|
410
|
+
// identifier down; the table already agreed, so the collapsed row was the
|
|
411
|
+
// only surface still printing them as prose.
|
|
412
|
+
//
|
|
413
|
+
// `intent` is a FILL, which is what a tag is — the hazard the other
|
|
414
|
+
// branches warn about is only ever using an intent as a TEXT colour.
|
|
415
|
+
//
|
|
416
|
+
// Falls through to the plain summary unless EVERY entry yields a name: a
|
|
417
|
+
// partly-chipped row would read as though the unnamed cases were missing.
|
|
418
|
+
if (valueType === 'test-cases' && Array.isArray(field?.value)) {
|
|
419
|
+
const caseNames = (field.value as unknown[]).map((item) => {
|
|
420
|
+
const name = (item as { name?: unknown } | null | undefined)?.name;
|
|
421
|
+
return typeof name === 'string' ? name.trim() : '';
|
|
422
|
+
});
|
|
423
|
+
if (caseNames.length > 0 && caseNames.every((name) => name !== '')) {
|
|
424
|
+
return (
|
|
425
|
+
<ReqoreControlGroup
|
|
426
|
+
gapSize='tiny'
|
|
427
|
+
verticalAlign='center'
|
|
428
|
+
// An editable row is ONE line: further chips clip with the row
|
|
429
|
+
// instead of growing it. A read-only row is the only rendering
|
|
430
|
+
// the value gets, so it wraps and shows every case.
|
|
431
|
+
wrap={full}
|
|
432
|
+
style={full ? undefined : { minWidth: 0, overflow: 'hidden' }}
|
|
433
|
+
>
|
|
434
|
+
{caseNames.map((name, index) => (
|
|
435
|
+
<ReqoreTag
|
|
436
|
+
key={`${name}-${index}`}
|
|
437
|
+
className='options-readfirst-case-tag'
|
|
438
|
+
size='small'
|
|
439
|
+
minimal
|
|
440
|
+
intent='info'
|
|
441
|
+
label={name}
|
|
442
|
+
/>
|
|
443
|
+
))}
|
|
444
|
+
</ReqoreControlGroup>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
354
449
|
// Code-editor read summary: replace the truncated string with a chip
|
|
355
450
|
// showing line count + character count. The actual code renders below
|
|
356
451
|
// the row as a monospace `<pre>` inside a `ReqoreCollapsibleContent` —
|
|
@@ -418,15 +513,43 @@ export const CompactRow = memo(
|
|
|
418
513
|
);
|
|
419
514
|
}
|
|
420
515
|
|
|
516
|
+
// The same, for an option whose mark is an ICON rather than a logo. Only
|
|
517
|
+
// images were drawn here, so a list whose entries are told apart by their
|
|
518
|
+
// icon — every interface kind is a bare word, `Service`, `Job`, `Qog` —
|
|
519
|
+
// showed the mark while choosing and lost it the moment the row went
|
|
520
|
+
// read-only, which is where a reader spends most of their time.
|
|
521
|
+
const allowedIcon = getAllowedValueIcon(field?.value, schema);
|
|
522
|
+
if (allowedIcon) {
|
|
523
|
+
return (
|
|
524
|
+
<span style={wrapStyle}>
|
|
525
|
+
<ReqoreIcon icon={allowedIcon as any} size='16px' style={{ flexShrink: 0 }} />
|
|
526
|
+
<span style={textStyle}>{formatted}</span>
|
|
527
|
+
</span>
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
421
531
|
// Template value ($local:…): the read-only template picker — the SAME chip
|
|
422
532
|
// TemplateField renders when disabled, so a template reads identically here
|
|
423
533
|
// and in the editor (qorus-ide intent scheme: info / qorus purple).
|
|
534
|
+
//
|
|
535
|
+
// A field may also declare templates OF ITS OWN, and a value drawn from
|
|
536
|
+
// that list is just as much a template even when it does not look like
|
|
537
|
+
// `$name:key` — `isValueTemplate` is a guess at the shape of the built-in
|
|
538
|
+
// grammar, and a host's own grammar (a Qorus test's `$.order.id`) fails it.
|
|
539
|
+
// Asking the field's list whether it CONTAINS the value settles it without
|
|
540
|
+
// guessing at syntax, and hands back the entry whose label the chip prints,
|
|
541
|
+
// so a reference reads as the name it was chosen by here as well as in the
|
|
542
|
+
// editor rather than as a raw path.
|
|
543
|
+
const ownTemplates = (schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates;
|
|
544
|
+
// The grammar the field declares its references in, when it speaks one of
|
|
545
|
+
// its own — the same schema key its rich-text editor is given.
|
|
546
|
+
const templateGrammar = (schema as TFieldWithOwnTemplates | undefined)?.templateToken;
|
|
424
547
|
if (
|
|
425
548
|
typeof field?.value === 'string' &&
|
|
426
549
|
!(field as { is_expression?: boolean }).is_expression &&
|
|
427
|
-
isValueTemplate(field.value)
|
|
550
|
+
(isValueTemplate(field.value) || !!findTemplate(ownTemplates ?? {}, field.value))
|
|
428
551
|
) {
|
|
429
|
-
return <ReadOnlyTemplateTag value={field.value} templates={
|
|
552
|
+
return <ReadOnlyTemplateTag value={field.value} templates={ownTemplates} size='small' />;
|
|
430
553
|
}
|
|
431
554
|
|
|
432
555
|
// A LIST whose elements are rich-text envelopes is a list of strings, so it
|
|
@@ -449,81 +572,48 @@ export const CompactRow = memo(
|
|
|
449
572
|
if (valueType === 'richtext' && Array.isArray(field?.value)) {
|
|
450
573
|
const segments = richtextToSegments(field.value as never);
|
|
451
574
|
if (segments.some((segment) => segment.kind === 'tag')) {
|
|
452
|
-
return
|
|
453
|
-
<span style={{ ...wrapStyle, gap: 4, overflow: 'hidden' }}>
|
|
454
|
-
{segments.map((segment, index) =>
|
|
455
|
-
segment.kind === 'tag' ?
|
|
456
|
-
<ReqoreTag
|
|
457
|
-
key={index}
|
|
458
|
-
size='tiny'
|
|
459
|
-
// The chip's label has to be the same size as the prose it is
|
|
460
|
-
// embedded in. The wrapper centres boxes, so two different text
|
|
461
|
-
// sizes centred against each other cannot share a baseline, and
|
|
462
|
-
// the chips visibly float above the words. Inheriting the row's
|
|
463
|
-
// size makes centring align the baselines too, at whatever font
|
|
464
|
-
// size the consuming app uses.
|
|
465
|
-
style={{ fontSize: 'inherit' }}
|
|
466
|
-
icon='ExchangeDollarLine'
|
|
467
|
-
label={segment.text || segment.value}
|
|
468
|
-
tooltip={segment.value}
|
|
469
|
-
{...getTemplateTagStyle(
|
|
470
|
-
(templates ? findTemplate(templates, segment.value || '') : undefined)
|
|
471
|
-
?.metadata as TTemplateMeta | undefined
|
|
472
|
-
)}
|
|
473
|
-
/>
|
|
474
|
-
: <span key={index} style={{ whiteSpace: full ? 'pre-wrap' : 'pre' }}>
|
|
475
|
-
{full ? segment.text : collapseSummaryBreaks(segment.text)}
|
|
476
|
-
</span>
|
|
477
|
-
)}
|
|
478
|
-
</span>
|
|
479
|
-
);
|
|
575
|
+
return <TemplateText segments={segments} templates={ownTemplates} full={full} />;
|
|
480
576
|
}
|
|
481
577
|
}
|
|
482
578
|
|
|
483
|
-
// An expression
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
// Unconditionally: a surface with no catalogue (the Automation Hub
|
|
490
|
-
// template preview never fetches one) still shows the reference's own
|
|
491
|
-
// path, which is the point — a conditional chip is no chip at all
|
|
492
|
-
// exactly where the raw token looks worst.
|
|
579
|
+
// An expression reads as DPQL wherever it is shown without being edited:
|
|
580
|
+
// the same rendering its Explain panel and Preview use — monospace,
|
|
581
|
+
// coloured by the language server, template references as chips — so a
|
|
582
|
+
// collapsed row and the editor it opens never disagree about what the
|
|
583
|
+
// value looks like. See `DpqlRendering`.
|
|
493
584
|
if ((field as { is_expression?: boolean })?.is_expression && formatted) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
: segment
|
|
585
|
+
return withExpressionMarker(
|
|
586
|
+
<span style={{ minWidth: 0, flex: '1 1 auto', ...(full ? {} : { overflow: 'hidden' }) }}>
|
|
587
|
+
<DpqlRendering text={formatted} templates={ownTemplates} />
|
|
588
|
+
</span>
|
|
499
589
|
);
|
|
590
|
+
}
|
|
500
591
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
592
|
+
/* A value that MENTIONS a reference rather than being one.
|
|
593
|
+
|
|
594
|
+
The branch above answers "is this value a template?", and a predicate is
|
|
595
|
+
not: `$._case.mode != 'simulate'` is a comparison the author wrote with a
|
|
596
|
+
reference inside it. So it fell all the way through to the plain string
|
|
597
|
+
and the row printed the engine's own spelling of a value the author had
|
|
598
|
+
picked by name — the one thing the template mechanism exists to stop.
|
|
599
|
+
Reported against the Skip-when field of a test case, where the list
|
|
600
|
+
above the drawer already named it and the drawer contradicted it.
|
|
601
|
+
|
|
602
|
+
Drawn from `formatted` rather than from the raw value, so everything the
|
|
603
|
+
formatter already did — a joined list, a sensitive value's dots — still
|
|
604
|
+
holds, and only the references inside the resulting line become chips.
|
|
605
|
+
The author's own text around them survives exactly as typed. */
|
|
606
|
+
const templateSegments =
|
|
607
|
+
typeof formatted === 'string' && formatted ?
|
|
608
|
+
templateTextSegments(formatted, ownTemplates, templateGrammar)
|
|
609
|
+
: [];
|
|
610
|
+
if (templateSegments.some((segment) => segment.kind === 'tag')) {
|
|
611
|
+
return withExpressionMarker(
|
|
612
|
+
<TemplateText segments={templateSegments} templates={ownTemplates} full={full} />
|
|
613
|
+
);
|
|
524
614
|
}
|
|
525
615
|
|
|
526
|
-
return full ? <span style={textStyle}>{formatted}</span> : formatted;
|
|
616
|
+
return withExpressionMarker(full ? <span style={textStyle}>{formatted}</span> : formatted);
|
|
527
617
|
};
|
|
528
618
|
|
|
529
619
|
const schema = options?.[optionName];
|
|
@@ -540,37 +630,15 @@ export const CompactRow = memo(
|
|
|
540
630
|
[availableOptions?.[optionName], optionActions, optionName, schema]
|
|
541
631
|
);
|
|
542
632
|
// Which injected actions stay as inline buttons, and which move into the
|
|
543
|
-
// overflow menu
|
|
544
|
-
// hover-gated button is unreachable without a hover — and anything past the
|
|
545
|
-
// inline cap overflows regardless, so a consumer injecting ten actions can
|
|
546
|
-
// never push the row's value out of view.
|
|
633
|
+
// overflow menu — see `splitOptionActions`.
|
|
547
634
|
const [inlineOptionActions, menuOptionActions] = React.useMemo<
|
|
548
635
|
[IReqorePanelAction[], IReqorePanelAction[]]
|
|
549
636
|
>(
|
|
550
|
-
() =>
|
|
551
|
-
collapseOptionActions ?
|
|
552
|
-
[[], injectedOptionActions]
|
|
553
|
-
: [
|
|
554
|
-
injectedOptionActions.slice(0, MAX_INLINE_OPTION_ACTIONS),
|
|
555
|
-
injectedOptionActions.slice(MAX_INLINE_OPTION_ACTIONS),
|
|
556
|
-
],
|
|
637
|
+
() => splitOptionActions(injectedOptionActions, !!collapseOptionActions),
|
|
557
638
|
[collapseOptionActions, injectedOptionActions]
|
|
558
639
|
);
|
|
559
640
|
const injectedOptionActionMenuItems = React.useMemo<IReqoreDropdownItem[]>(
|
|
560
|
-
() =>
|
|
561
|
-
menuOptionActions.map(
|
|
562
|
-
(action, index) =>
|
|
563
|
-
({
|
|
564
|
-
// A panel action labels itself with `label`, but an icon-only one
|
|
565
|
-
// (the IDE's AI-assist button) carries its name in the tooltip —
|
|
566
|
-
// a menu row has no hover affordance to fall back on.
|
|
567
|
-
label: action.label ?? action.tooltip ?? `Action ${index + 1}`,
|
|
568
|
-
icon: action.icon,
|
|
569
|
-
intent: action.intent,
|
|
570
|
-
disabled: action.disabled,
|
|
571
|
-
onClick: () => action.onClick?.(),
|
|
572
|
-
}) as IReqoreDropdownItem
|
|
573
|
-
),
|
|
641
|
+
() => menuOptionActions.map(optionActionAsMenuItem),
|
|
574
642
|
[menuOptionActions]
|
|
575
643
|
);
|
|
576
644
|
const renderInjectedOptionAction = (
|
|
@@ -627,8 +695,23 @@ export const CompactRow = memo(
|
|
|
627
695
|
const changed = !hidden && !readOnly && hasOptionChanged(optionField?.value, optionName);
|
|
628
696
|
// "Has a value" = set to anything non-empty. Drives the edit-row Clear
|
|
629
697
|
// button and the cluster node's filled state (see `memberSet`).
|
|
698
|
+
/* "Is there anything to clear?" — this predicate has exactly one job, and
|
|
699
|
+
both of its uses are the Clear-value button.
|
|
700
|
+
|
|
701
|
+
An empty list is not a value. `[]` is neither `undefined`, `null` nor `''`,
|
|
702
|
+
so a field whose last item had just been removed went on offering to clear
|
|
703
|
+
it, and the confirmation asked whether to clear nothing. A test's cases
|
|
704
|
+
table is where this shows: delete the last case and a red clear-all sat
|
|
705
|
+
above an empty table, guarding a no-op.
|
|
706
|
+
|
|
707
|
+
Deliberately arrays only. An empty hash is arguable — "present but empty"
|
|
708
|
+
can be a real state for a hash — and nothing has demonstrated the same
|
|
709
|
+
defect there, so widening this predicate would be a guess. */
|
|
630
710
|
const hasValue =
|
|
631
|
-
optionField?.value !== undefined &&
|
|
711
|
+
optionField?.value !== undefined &&
|
|
712
|
+
optionField?.value !== null &&
|
|
713
|
+
optionField?.value !== '' &&
|
|
714
|
+
!(Array.isArray(optionField.value) && optionField.value.length === 0);
|
|
632
715
|
// Required-group membership shows a PERSISTENT chip on every member: amber
|
|
633
716
|
// "One of" while the group is unmet (tap → flash siblings), then a muted-green
|
|
634
717
|
// resolution once satisfied — "Covers" on the field that satisfied it,
|
|
@@ -705,7 +788,7 @@ export const CompactRow = memo(
|
|
|
705
788
|
: (schema?.ui_type as string) || (schema?.type as string)) ?? '';
|
|
706
789
|
// Scalars edit in place inside the row; complex fields (tall or nested
|
|
707
790
|
// editors) still open the expanded card below.
|
|
708
|
-
const
|
|
791
|
+
const inlineEditableNow =
|
|
709
792
|
!readOnly &&
|
|
710
793
|
!schema?.arg_schema &&
|
|
711
794
|
!(operators && size(operators)) &&
|
|
@@ -715,22 +798,160 @@ export const CompactRow = memo(
|
|
|
715
798
|
!optionHasImages(schema) &&
|
|
716
799
|
!COMPACT_COMPLEX_TYPES.has(editType);
|
|
717
800
|
|
|
718
|
-
|
|
719
|
-
|
|
801
|
+
/* Which of the two editors a row gets is settled when the row OPENS.
|
|
802
|
+
*
|
|
803
|
+
* Every term above reads the VALUE or a schema derived from it, so the
|
|
804
|
+
* answer can change while the author is inside the control — and the two
|
|
805
|
+
* branches are different subtrees, so a changed answer does not restyle
|
|
806
|
+
* the editor, it unmounts one and mounts the other. Everything the old one
|
|
807
|
+
* held goes with it: what was typed, the caret, and any state the editor
|
|
808
|
+
* keeps about how it was entered.
|
|
809
|
+
*
|
|
810
|
+
* `is_expression` is the term that actually fires, and it fires on the one
|
|
811
|
+
* interaction that exists to keep the author where they are: accepting
|
|
812
|
+
* "Use as expression" is the moment the value becomes an expression, and
|
|
813
|
+
* the expression shell opens on its Text view precisely because the author
|
|
814
|
+
* has just written the expression as text. The remount discarded that and
|
|
815
|
+
* dropped them into the visual builder, so the sentence they had typed was
|
|
816
|
+
* nowhere on screen. Clicking Text then appeared to work and bounced back,
|
|
817
|
+
* because the next value change remounted the row again.
|
|
818
|
+
*
|
|
819
|
+
* So the row keeps the editor it opened with for as long as it is open,
|
|
820
|
+
* and asks the question again when it is next opened — the same trade
|
|
821
|
+
* `editEntryValue` above makes for a different reason, and the one
|
|
822
|
+
* qorus-ide's `referenceEditorLatch` makes for this exact one. A field that
|
|
823
|
+
* already holds an expression when it is opened still gets the card; it is
|
|
824
|
+
* only a field that BECOMES one under the author that stays put.
|
|
825
|
+
*
|
|
826
|
+
* Held in a ref and settled during render rather than in an effect: an
|
|
827
|
+
* effect lands one render late, and one render late is a visible flash of
|
|
828
|
+
* the wrong editor — which is the thing being fixed.
|
|
829
|
+
*/
|
|
830
|
+
const inlineEditableLatch = React.useRef<boolean | undefined>(undefined);
|
|
831
|
+
/* Whether THIS opening was the author asking to change the value.
|
|
832
|
+
*
|
|
833
|
+
* A click (or Enter/Space) on a closed row means "let me edit this"; a row
|
|
834
|
+
* opened by the form itself — `initialExpandedOptions` from an address, the
|
|
835
|
+
* first-attention row, a diagram opening the step it is showing — means
|
|
836
|
+
* "here it is". Only the first answers the question the picker answers, so
|
|
837
|
+
* only the first opens one (see the caret effect below). A ref, because it
|
|
838
|
+
* is read by the effect that runs after the open and must not itself cause
|
|
839
|
+
* a render. */
|
|
840
|
+
const openedByAuthor = React.useRef(false);
|
|
841
|
+
if (!isExpanded) {
|
|
842
|
+
inlineEditableLatch.current = undefined;
|
|
843
|
+
} else if (inlineEditableLatch.current === undefined) {
|
|
844
|
+
inlineEditableLatch.current = inlineEditableNow;
|
|
845
|
+
}
|
|
846
|
+
const inlineEditable = isExpanded ? !!inlineEditableLatch.current : inlineEditableNow;
|
|
847
|
+
|
|
848
|
+
/* The author's intent survives until the row is closed again, and no longer.
|
|
849
|
+
*
|
|
850
|
+
* An EFFECT, keyed on the row actually closing, rather than a line in the
|
|
851
|
+
* render body: between the click and the row opening there is a committed
|
|
852
|
+
* render that still reads `expandedOptions` without this row in it (the
|
|
853
|
+
* context propagates a beat after the handler runs), and clearing the flag
|
|
854
|
+
* on every collapsed render cleared it in that gap — the picker then never
|
|
855
|
+
* opened, which is the defect this whole mechanism exists to fix. Keyed on
|
|
856
|
+
* `isExpanded`, the transient render is not a change and the effect does
|
|
857
|
+
* not run; a genuine collapse is, and it does. */
|
|
858
|
+
React.useEffect(() => {
|
|
859
|
+
if (!isExpanded) {
|
|
860
|
+
openedByAuthor.current = false;
|
|
861
|
+
}
|
|
862
|
+
}, [isExpanded]);
|
|
863
|
+
|
|
864
|
+
/* The instruction the row gives its editor when it opens into a picker.
|
|
865
|
+
*
|
|
866
|
+
* State, not a call on the control: reqore's popover binds its own click
|
|
867
|
+
* listener in an effect keyed on the trigger element arriving, so a
|
|
868
|
+
* synthetic click dispatched from the row's own effect lands before
|
|
869
|
+
* anything is listening and does nothing at all. The editor opening itself
|
|
870
|
+
* from a prop has no such ordering to lose. */
|
|
871
|
+
const [openThePicker, setOpenThePicker] = React.useState(false);
|
|
872
|
+
React.useEffect(() => {
|
|
873
|
+
// One shot: true for the commit the editor acts on, false again after.
|
|
874
|
+
if (openThePicker) {
|
|
875
|
+
setOpenThePicker(false);
|
|
876
|
+
}
|
|
877
|
+
}, [openThePicker]);
|
|
878
|
+
|
|
879
|
+
// Put the caret in the row's editor when the row opens, so the reader can
|
|
880
|
+
// answer straight away (the prototype's tap-to-edit feel) — and so
|
|
881
|
+
// `autoFocusFirstRequired` means what it says.
|
|
882
|
+
//
|
|
883
|
+
// Whatever control the row OFFERS: `findRowFocusTarget` knows a pick-one is
|
|
884
|
+
// a set of `[tabindex]` checkboxes and a selector is a `<button>`, which a
|
|
885
|
+
// text-only scan skipped — so the engine opened the right row and the caret
|
|
886
|
+
// never moved. That is most of the first questions a form asks.
|
|
887
|
+
//
|
|
888
|
+
// The row is revealed before the caret lands in it, `block: 'nearest'` on
|
|
889
|
+
// the ROW. A caret nobody can see is not a caret: a form opened by a click
|
|
890
|
+
// usually mounts below the fold, and focusing something 700px down changes
|
|
891
|
+
// nothing on screen while the next keystroke goes somewhere invisible.
|
|
892
|
+
// `nearest` is by spec a no-op for a row already in view, so a form on
|
|
893
|
+
// screen never jumps, and it is the row that carries the label saying what
|
|
894
|
+
// the question is. A caller that wants the row opened WITHOUT the caret
|
|
895
|
+
// has `expandFirstRequired` for exactly that.
|
|
896
|
+
//
|
|
897
|
+
// A MutationObserver, not a timer: a host editor that resolves a catalogue
|
|
898
|
+
// before it can draw a control mounts it several commits after this effect
|
|
899
|
+
// runs, and there is no fixed time to wait for it — the control appearing
|
|
900
|
+
// IS the event. The 60ms timer this replaces missed every such editor, and
|
|
901
|
+
// fired too early or too late for every other one. One shot, and it gives
|
|
902
|
+
// up the moment the caret belongs to someone outside this row, so it can
|
|
903
|
+
// never pull a reader out of a control they moved into themselves.
|
|
720
904
|
const editorRef = React.useRef<HTMLDivElement>(null);
|
|
905
|
+
const rowRef = React.useRef<HTMLDivElement>(null);
|
|
721
906
|
React.useEffect(() => {
|
|
722
907
|
if (!isExpanded) return undefined;
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
908
|
+
const editor = editorRef.current;
|
|
909
|
+
if (!editor) return undefined;
|
|
910
|
+
let settled = false;
|
|
911
|
+
const takeCaret = (): boolean => {
|
|
912
|
+
if (settled) return true;
|
|
913
|
+
const active = document.activeElement as HTMLElement | null;
|
|
914
|
+
if (active && active !== document.body && !rowRef.current?.contains(active)) {
|
|
915
|
+
// Someone else holds the caret — a field the reader moved into while
|
|
916
|
+
// this row's editor was still arriving. Leave it where it is.
|
|
917
|
+
settled = true;
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
const control = findRowFocusTarget(editor);
|
|
921
|
+
if (!control) return false;
|
|
922
|
+
// Optional call: jsdom does not implement `scrollIntoView`.
|
|
923
|
+
rowRef.current?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
|
|
924
|
+
// `preventScroll`, because the reveal above has already put the row
|
|
925
|
+
// where it belongs; letting focus scroll as well moves it twice.
|
|
926
|
+
control.focus({ preventScroll: true });
|
|
927
|
+
/* …and if the control the row offers is a PICKER, open it.
|
|
928
|
+
*
|
|
929
|
+
* A closed picker prints the value it holds, which is exactly what the
|
|
930
|
+
* read row printed — so the row opened, nothing on screen changed, and
|
|
931
|
+
* changing the value still cost a second click. Read-first is the
|
|
932
|
+
* COLLAPSED row's job; an open row is the editor.
|
|
933
|
+
*
|
|
934
|
+
* Narrow on purpose, and each term is a case where read-first is still
|
|
935
|
+
* right: only a row the author opened themselves (a form that opens a
|
|
936
|
+
* row to SHOW it must not throw a list over it), only an inline scalar
|
|
937
|
+
* row (a complex field opens a card that is a form in its own right —
|
|
938
|
+
* its first control is one of several and nobody asked for it), and
|
|
939
|
+
* only a control that declares itself a picker, so nothing else is
|
|
940
|
+
* ever clicked on the author's behalf. */
|
|
941
|
+
if (openedByAuthor.current && inlineEditable && isClosedPickerTrigger(control)) {
|
|
942
|
+
openedByAuthor.current = false;
|
|
943
|
+
setOpenThePicker(true);
|
|
944
|
+
}
|
|
945
|
+
settled = true;
|
|
946
|
+
return true;
|
|
947
|
+
};
|
|
948
|
+
if (takeCaret() || typeof MutationObserver === 'undefined') return undefined;
|
|
949
|
+
const observer = new MutationObserver(() => {
|
|
950
|
+
if (takeCaret()) observer.disconnect();
|
|
951
|
+
});
|
|
952
|
+
observer.observe(editor, { childList: true, subtree: true });
|
|
953
|
+
return () => observer.disconnect();
|
|
954
|
+
}, [isExpanded, optionName, inlineEditable]);
|
|
734
955
|
|
|
735
956
|
// What the field held when it was opened — the baseline Cancel restores.
|
|
736
957
|
//
|
|
@@ -1061,6 +1282,19 @@ export const CompactRow = memo(
|
|
|
1061
1282
|
// stacks children flush, and the class rule that used to carry a 4px gap
|
|
1062
1283
|
// never reached this element at all — which is why two messages read as one
|
|
1063
1284
|
// two-tone block with no space between them.
|
|
1285
|
+
/* A message is PROSE, so it is drawn by the host's prose renderer.
|
|
1286
|
+
|
|
1287
|
+
`markdownRendererContext` says this package renders markdown "in exactly
|
|
1288
|
+
one place — a field's description". That was one place short: a schema
|
|
1289
|
+
message is written by the same author, in the same dialect, and it landed
|
|
1290
|
+
here as a raw string. A Qorus warning reading "Give that step a **Fixture
|
|
1291
|
+
Output**" showed the asterisks, and every reference inside it stayed a raw
|
|
1292
|
+
`$.` path where the editor two lines above renders it as a named chip.
|
|
1293
|
+
|
|
1294
|
+
`compact` for the same reason the row inset asks for it: this is a strip
|
|
1295
|
+
inside a row, never a page, and markdown authored as a document opens with
|
|
1296
|
+
a heading that would outgrow the field label above it. Without a host
|
|
1297
|
+
renderer the string is drawn as it always was. */
|
|
1064
1298
|
const renderInfoStrip = (m: TInfoMsg, index: number) => (
|
|
1065
1299
|
<ReqoreMessage
|
|
1066
1300
|
key={`${m.content}-${index}`}
|
|
@@ -1071,7 +1305,9 @@ export const CompactRow = memo(
|
|
|
1071
1305
|
title={m.title}
|
|
1072
1306
|
style={{ marginBottom: 8 }}
|
|
1073
1307
|
>
|
|
1074
|
-
{m.content
|
|
1308
|
+
{markdownRenderer && typeof m.content === 'string' ?
|
|
1309
|
+
markdownRenderer({ value: m.content, compact: true })
|
|
1310
|
+
: m.content}
|
|
1075
1311
|
</ReqoreMessage>
|
|
1076
1312
|
);
|
|
1077
1313
|
const reasonColor = (intent?: string) =>
|
|
@@ -1108,6 +1344,9 @@ export const CompactRow = memo(
|
|
|
1108
1344
|
}
|
|
1109
1345
|
: {};
|
|
1110
1346
|
|
|
1347
|
+
// Items the EDITOR inside this row published — see `rowMenuContext`.
|
|
1348
|
+
const { rowMenu, items: editorMenuItems } = useRowMenuRegistry();
|
|
1349
|
+
|
|
1111
1350
|
// Secondary edit actions tuck into a "More" (⋮) menu so the card header stays
|
|
1112
1351
|
// calm: Fullscreen always, plus Remove field for a removable option. Rendered
|
|
1113
1352
|
// just before the Done ✓ in both expanded layouts.
|
|
@@ -1144,6 +1383,10 @@ export const CompactRow = memo(
|
|
|
1144
1383
|
// wholesale on touch) land here — this row already has a menu, so they
|
|
1145
1384
|
// reuse it rather than adding a second one beside it.
|
|
1146
1385
|
...injectedOptionActionMenuItems,
|
|
1386
|
+
// ...and for the same reason, so do the editor's own actions. Without
|
|
1387
|
+
// this an editor with affordances of its own had to draw a second ⋮
|
|
1388
|
+
// inside the value cell, beside this one.
|
|
1389
|
+
...editorMenuItems,
|
|
1147
1390
|
]}
|
|
1148
1391
|
/>
|
|
1149
1392
|
);
|
|
@@ -1218,6 +1461,7 @@ export const CompactRow = memo(
|
|
|
1218
1461
|
const editingRow = (
|
|
1219
1462
|
<div
|
|
1220
1463
|
key={optionName}
|
|
1464
|
+
ref={rowRef}
|
|
1221
1465
|
data-field={optionName}
|
|
1222
1466
|
className='readfirst-row readfirst-row-editing options-readfirst-inline options-readfirst-value'
|
|
1223
1467
|
style={
|
|
@@ -1316,7 +1560,17 @@ export const CompactRow = memo(
|
|
|
1316
1560
|
editor they belong to — the language you are writing in is
|
|
1317
1561
|
read before the code, not after it. */}
|
|
1318
1562
|
{absorbedNodes}
|
|
1319
|
-
{
|
|
1563
|
+
{/* The editor may publish into THIS row's ⋮ — see `rowMenuContext`.
|
|
1564
|
+
Only the editors rendered in the row are given the channel: the
|
|
1565
|
+
fullscreen modal below has no row menu on screen, so an editor
|
|
1566
|
+
there keeps drawing its own control. */}
|
|
1567
|
+
{/* Scoped to the row's OWN editor: the absorbed siblings above
|
|
1568
|
+
are other fields, which this row did not open. */}
|
|
1569
|
+
<RowOpenPickerContext.Provider value={openThePicker}>
|
|
1570
|
+
<RowMenuContext.Provider value={rowMenu}>
|
|
1571
|
+
{renderOption(optionName, optionField, 'small', true)}
|
|
1572
|
+
</RowMenuContext.Provider>
|
|
1573
|
+
</RowOpenPickerContext.Provider>
|
|
1320
1574
|
</div>
|
|
1321
1575
|
<StyledRowActions>
|
|
1322
1576
|
{draftChip}
|
|
@@ -1556,7 +1810,9 @@ export const CompactRow = memo(
|
|
|
1556
1810
|
longDescriptionShownByDefault
|
|
1557
1811
|
/>
|
|
1558
1812
|
: null}
|
|
1559
|
-
{
|
|
1813
|
+
<RowMenuContext.Provider value={rowMenu}>
|
|
1814
|
+
{renderOption(optionName, optionField)}
|
|
1815
|
+
</RowMenuContext.Provider>
|
|
1560
1816
|
</FocusedEditing>
|
|
1561
1817
|
</div>
|
|
1562
1818
|
</StyledEditCard>
|
|
@@ -1596,11 +1852,21 @@ export const CompactRow = memo(
|
|
|
1596
1852
|
];
|
|
1597
1853
|
// A hash row reveals its sub-fields as read-only sub-rows under a "view
|
|
1598
1854
|
// more" disclosure; the row itself still expands the real editor on click.
|
|
1855
|
+
/* An EXPRESSION is not a hash, whatever shape it is stored in.
|
|
1856
|
+
|
|
1857
|
+
`{is_expression: true, value: {exp, args}}` is hash-shaped, so a field
|
|
1858
|
+
holding one earned the structured inset and the row printed the syntax
|
|
1859
|
+
tree — `is_expression true / value / exp + / args 1 2` — directly under a
|
|
1860
|
+
summary line that already read `1 + 2`. The AST is how an expression is
|
|
1861
|
+
STORED, not what it is; the row's line is the whole value, and the
|
|
1862
|
+
expression editor is where its parts are looked at. Same exclusion, and
|
|
1863
|
+
for the same reason, as `schema-definition` beside it. */
|
|
1599
1864
|
const hashEntries =
|
|
1600
1865
|
(
|
|
1601
1866
|
!hidden &&
|
|
1602
1867
|
(valueType === 'hash' || valueType === 'free-hash') &&
|
|
1603
|
-
(schema as { ui_type?: string } | undefined)?.ui_type !== 'schema-definition'
|
|
1868
|
+
(schema as { ui_type?: string } | undefined)?.ui_type !== 'schema-definition' &&
|
|
1869
|
+
getExpressionAst(optionField) === undefined
|
|
1604
1870
|
) ?
|
|
1605
1871
|
getHashEntries(optionField, schema)
|
|
1606
1872
|
: [];
|
|
@@ -1647,6 +1913,35 @@ export const CompactRow = memo(
|
|
|
1647
1913
|
valueType === 'richtext' &&
|
|
1648
1914
|
Array.isArray(optionField?.value) &&
|
|
1649
1915
|
richtextToSegments(optionField.value as never).some((segment) => segment.kind === 'tag');
|
|
1916
|
+
/* The same pair, for a value that IS one template rather than prose with
|
|
1917
|
+
templates embedded in it. `showsTemplateChips` above closed the richtext
|
|
1918
|
+
case and left this one open: a chosen reference renders as
|
|
1919
|
+
`ReadOnlyTemplateTag`, which carries its own popover, while the row went
|
|
1920
|
+
on setting a native `title` holding the raw reference. Hovering fired
|
|
1921
|
+
both — two tooltips in two places, and the one that says something useful
|
|
1922
|
+
is not the one a screenshot captures. */
|
|
1923
|
+
const showsTemplateTag =
|
|
1924
|
+
!hidden &&
|
|
1925
|
+
typeof optionField?.value === 'string' &&
|
|
1926
|
+
!(optionField as { is_expression?: boolean } | undefined)?.is_expression &&
|
|
1927
|
+
(isValueTemplate(optionField.value) ||
|
|
1928
|
+
!!findTemplate(
|
|
1929
|
+
(schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates ?? {},
|
|
1930
|
+
optionField.value
|
|
1931
|
+
));
|
|
1932
|
+
/* And once more for a value that only MENTIONS references — a predicate, a
|
|
1933
|
+
greeting with a name in it. The row draws those as prose plus chips too,
|
|
1934
|
+
so a native `title` would hover the raw spelling of exactly what the chips
|
|
1935
|
+
replaced, and each chip's own popover would fire alongside it. */
|
|
1936
|
+
const showsTemplateText =
|
|
1937
|
+
!hidden &&
|
|
1938
|
+
!showsTemplateTag &&
|
|
1939
|
+
!!formatted &&
|
|
1940
|
+
hasTemplateText(
|
|
1941
|
+
formatted,
|
|
1942
|
+
(schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates,
|
|
1943
|
+
(schema as TFieldWithOwnTemplates | undefined)?.templateToken
|
|
1944
|
+
);
|
|
1650
1945
|
// Markdown reads the same way for the same reason: the row itself can only
|
|
1651
1946
|
// show a line of text, and for markdown that line is the SOURCE — the reader
|
|
1652
1947
|
// gets `## ` and `**` where the point of the value is what it looks like
|
|
@@ -1749,9 +2044,16 @@ export const CompactRow = memo(
|
|
|
1749
2044
|
return {
|
|
1750
2045
|
name: depName,
|
|
1751
2046
|
exists: !!options?.[depName],
|
|
1752
|
-
|
|
2047
|
+
// `!name` compares against nothing — it is satisfied by the sibling
|
|
2048
|
+
// being unanswered — so it says so, rather than going through the
|
|
2049
|
+
// comparison template and reading `Name ≠ undefined`.
|
|
2050
|
+
label:
|
|
2051
|
+
op === '!' ? `${depLabel} unset`
|
|
2052
|
+
: !op ? depLabel
|
|
2053
|
+
: `${depLabel} ${op === '=' ? '=' : '≠'} ${expected}`,
|
|
1753
2054
|
fulfilled:
|
|
1754
|
-
|
|
2055
|
+
op === '!' ? isOptionValueEmpty(depValue)
|
|
2056
|
+
: !op ? !isOptionValueEmpty(depValue)
|
|
1755
2057
|
: depValue == null ? false
|
|
1756
2058
|
: op === '=' ? String(depValue) === expected
|
|
1757
2059
|
: String(depValue) !== expected,
|
|
@@ -1812,14 +2114,26 @@ export const CompactRow = memo(
|
|
|
1812
2114
|
// with `readOnly` handed down (an id in a greyed-out input, a choice
|
|
1813
2115
|
// that could still be changed); then into a read view of the same
|
|
1814
2116
|
// value — which showed nothing the row could not show itself once it
|
|
1815
|
-
// stopped truncating. So it shows everything, and opens nothing.
|
|
2117
|
+
// stopped truncating. So it shows everything, and opens nothing HERE.
|
|
2118
|
+
//
|
|
2119
|
+
// Where the consumer says the field is editable somewhere else, the row
|
|
2120
|
+
// is the way in to that place: a reader clicking a value they want to
|
|
2121
|
+
// change has said exactly what they want, and answering with silence
|
|
2122
|
+
// teaches them the surface is broken.
|
|
1816
2123
|
if (fieldDisabled || readOnly) {
|
|
2124
|
+
if (readOnly && !fieldDisabled) {
|
|
2125
|
+
onReadOnlyActivate?.(optionName);
|
|
2126
|
+
}
|
|
1817
2127
|
return;
|
|
1818
2128
|
}
|
|
1819
2129
|
const target = event?.currentTarget as HTMLElement | undefined;
|
|
1820
2130
|
if (target?.classList?.contains('readfirst-row')) {
|
|
1821
2131
|
readRowHeights.current[optionName] = Math.round(target.getBoundingClientRect().height);
|
|
1822
2132
|
}
|
|
2133
|
+
// The author asked for this one — so the row opens all the way (a picker
|
|
2134
|
+
// opens its list rather than reprinting the value as a closed trigger).
|
|
2135
|
+
// Only when it is OPENING: the same handler collapses an open row.
|
|
2136
|
+
openedByAuthor.current = !isExpanded;
|
|
1823
2137
|
// Optional fields aren't "added" — every one is editable in place. Opening a
|
|
1824
2138
|
// not-yet-set field just expands its editor (with an empty value); it joins
|
|
1825
2139
|
// the form's output the moment a value is set (and moves to Set / Needs
|
|
@@ -1876,19 +2190,30 @@ export const CompactRow = memo(
|
|
|
1876
2190
|
: dotStatus === 'todo' ? cWarning
|
|
1877
2191
|
: dotStatus === 'set' ? cSuccess || cInfo
|
|
1878
2192
|
: undefined;
|
|
2193
|
+
/* A read-only row is a control only when the consumer gave it somewhere to
|
|
2194
|
+
go. Then it takes a button's role, focus and keyboard handling, because
|
|
2195
|
+
a thing that acts on a click has to be reachable without a mouse and has
|
|
2196
|
+
to announce what it does — and "Edit X", not "X", is what it does. */
|
|
2197
|
+
const readOnlyIsAWayIn = !!readOnly && !fieldDisabled && !!onReadOnlyActivate;
|
|
2198
|
+
const actsOnClick = !readOnly || readOnlyIsAWayIn;
|
|
2199
|
+
|
|
1879
2200
|
const row = (
|
|
1880
2201
|
<div
|
|
1881
2202
|
key={optionName}
|
|
1882
2203
|
data-field={optionName}
|
|
1883
|
-
role={
|
|
1884
|
-
tabIndex={
|
|
1885
|
-
aria-label={
|
|
1886
|
-
|
|
2204
|
+
role={actsOnClick ? 'button' : undefined}
|
|
2205
|
+
tabIndex={actsOnClick ? 0 : undefined}
|
|
2206
|
+
aria-label={
|
|
2207
|
+
readOnlyIsAWayIn ? `Edit ${label}`
|
|
2208
|
+
: readOnly ? undefined
|
|
2209
|
+
: label
|
|
2210
|
+
}
|
|
2211
|
+
className={`readfirst-row options-readfirst-value${readOnly ? ' readfirst-row-read' : ''}${readOnlyIsAWayIn ? ' readfirst-row-way-in' : ''}${hidden ? ' readfirst-row-hidden' : ''}${fieldDisabled ? ' readfirst-row-disabled' : ''}${isHighlighted ? ' readfirst-row-group-highlight' : ''}${isFlashed ? ' readfirst-row-flash' : ''}${showLabelDesc ? ' readfirst-row-info-open' : ''}${panelMessages.length || showStructuredPreview || showCodePreview || showMarkdownPreview || showLongTextInset ? ' readfirst-row-tall' : ''}${clusterBlockClass ? ' ' + clusterBlockClass : ''}`}
|
|
1887
2212
|
aria-disabled={fieldDisabled || undefined}
|
|
1888
|
-
style={stripeStyle}
|
|
1889
|
-
onClick={
|
|
2213
|
+
style={readOnlyIsAWayIn ? { ...stripeStyle, cursor: 'pointer' } : stripeStyle}
|
|
2214
|
+
onClick={actsOnClick ? activate : undefined}
|
|
1890
2215
|
onKeyDown={
|
|
1891
|
-
|
|
2216
|
+
!actsOnClick ? undefined : (
|
|
1892
2217
|
(event) => {
|
|
1893
2218
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
1894
2219
|
event.preventDefault();
|
|
@@ -1960,6 +2285,8 @@ export const CompactRow = memo(
|
|
|
1960
2285
|
!showCodePreview &&
|
|
1961
2286
|
!showMarkdownPreview &&
|
|
1962
2287
|
!showsTemplateChips &&
|
|
2288
|
+
!showsTemplateTag &&
|
|
2289
|
+
!showsTemplateText &&
|
|
1963
2290
|
typeof formatted === 'string'
|
|
1964
2291
|
) ?
|
|
1965
2292
|
formatted
|