@qoretechnologies/reqraft 0.10.66-pr.145.gcb1e59d → 0.10.66
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/README.md +53 -1
- package/dist/components/Description/AllowedValuesHelp.d.ts +20 -0
- package/dist/components/Description/AllowedValuesHelp.d.ts.map +1 -0
- package/dist/components/Description/AllowedValuesHelp.js +86 -0
- package/dist/components/Description/AllowedValuesHelp.js.map +1 -0
- package/dist/components/Description/describedControl.d.ts +54 -0
- package/dist/components/Description/describedControl.d.ts.map +1 -0
- package/dist/components/Description/describedControl.js +81 -0
- package/dist/components/Description/describedControl.js.map +1 -0
- package/dist/components/Description/index.d.ts.map +1 -1
- package/dist/components/Description/index.js +3 -2
- package/dist/components/Description/index.js.map +1 -1
- package/dist/components/colorValue/colorValue.d.ts +59 -0
- package/dist/components/colorValue/colorValue.d.ts.map +1 -0
- package/dist/components/colorValue/colorValue.js +245 -0
- package/dist/components/colorValue/colorValue.js.map +1 -0
- package/dist/components/colorValue/index.d.ts +34 -0
- package/dist/components/colorValue/index.d.ts.map +1 -0
- package/dist/components/colorValue/index.js +136 -0
- package/dist/components/colorValue/index.js.map +1 -0
- package/dist/components/colorValue/labels.d.ts +12 -0
- package/dist/components/colorValue/labels.d.ts.map +1 -0
- package/dist/components/colorValue/labels.js +11 -0
- package/dist/components/colorValue/labels.js.map +1 -0
- package/dist/components/composer/Composer.d.ts +1 -1
- package/dist/components/composer/Composer.d.ts.map +1 -1
- package/dist/components/composer/Composer.js +2 -2
- 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 +156 -10
- package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlCompletions.d.ts +39 -0
- package/dist/components/dpqlEditor/dpqlCompletions.d.ts.map +1 -0
- package/dist/components/dpqlEditor/dpqlCompletions.js +294 -0
- package/dist/components/dpqlEditor/dpqlCompletions.js.map +1 -0
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts +54 -5
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.js +420 -63
- package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlInserter.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlInserter.js +67 -29
- package/dist/components/dpqlEditor/dpqlInserter.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlTags.d.ts +17 -7
- package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlTags.js +44 -17
- package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
- package/dist/components/dpqlEditor/types.d.ts +94 -8
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.d.ts +7 -2
- package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.js +49 -17
- 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 +336 -243
- 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 +20 -57
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +67 -59
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +606 -409
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/HiddenFieldsNote.d.ts +28 -0
- package/dist/components/form/engine/HiddenFieldsNote.d.ts.map +1 -0
- package/dist/components/form/engine/HiddenFieldsNote.js +32 -0
- package/dist/components/form/engine/HiddenFieldsNote.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +2 -18
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +28 -25
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/OptionsHelpDialog.d.ts.map +1 -1
- package/dist/components/form/engine/OptionsHelpDialog.js +19 -2
- package/dist/components/form/engine/OptionsHelpDialog.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts +2 -2
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +11 -31
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +30 -6
- 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 +56 -31
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +121 -110
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarColors.d.ts +16 -0
- package/dist/components/form/engine/compactToolbarColors.d.ts.map +1 -0
- package/dist/components/form/engine/compactToolbarColors.js +21 -0
- package/dist/components/form/engine/compactToolbarColors.js.map +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts +0 -3
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/inheritProps.d.ts +29 -0
- package/dist/components/form/engine/inheritProps.d.ts.map +1 -0
- package/dist/components/form/engine/inheritProps.js +43 -0
- package/dist/components/form/engine/inheritProps.js.map +1 -0
- package/dist/components/form/engine/operatorSummary.d.ts +26 -0
- package/dist/components/form/engine/operatorSummary.d.ts.map +1 -0
- package/dist/components/form/engine/operatorSummary.js +35 -0
- package/dist/components/form/engine/operatorSummary.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +26 -22
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +79 -115
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/readFirstColors.d.ts +30 -0
- package/dist/components/form/engine/readFirstColors.d.ts.map +1 -0
- package/dist/components/form/engine/readFirstColors.js +51 -0
- package/dist/components/form/engine/readFirstColors.js.map +1 -0
- package/dist/components/form/engine/rowFocus.d.ts +3 -9
- package/dist/components/form/engine/rowFocus.d.ts.map +1 -1
- package/dist/components/form/engine/rowFocus.js +30 -19
- package/dist/components/form/engine/rowFocus.js.map +1 -1
- package/dist/components/form/engine/typeChoices.d.ts +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.js +2 -1
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -1
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -1
- package/dist/components/form/engine/variants/VariantCards.js +2 -1
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -1
- package/dist/components/form/engine/variants/VariantFocus.js +1 -1
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -1
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -1
- package/dist/components/form/engine/variants/VariantMinimal.js +2 -1
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -1
- package/dist/components/form/engine/variants/variantParts.d.ts +1 -1
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -1
- package/dist/components/form/engine/variants/variantParts.js +25 -17
- package/dist/components/form/engine/variants/variantParts.js.map +1 -1
- package/dist/components/form/expressions/DpqlRendering.d.ts +1 -1
- package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -1
- package/dist/components/form/expressions/DpqlRendering.js +7 -6
- package/dist/components/form/expressions/DpqlRendering.js.map +1 -1
- package/dist/components/form/expressions/ExpressionField.d.ts +67 -3
- package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
- package/dist/components/form/expressions/ExpressionField.js +536 -114
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +14 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -1
- package/dist/components/form/expressions/argumentPresence.js +16 -1
- package/dist/components/form/expressions/argumentPresence.js.map +1 -1
- package/dist/components/form/expressions/builder/argumentDetail.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentDetail.js +4 -1
- package/dist/components/form/expressions/builder/argumentDetail.js.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.js +30 -11
- package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
- package/dist/components/form/expressions/builder/argumentWrapper.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentWrapper.js +3 -1
- package/dist/components/form/expressions/builder/argumentWrapper.js.map +1 -1
- package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.d.ts +9 -0
- package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.js +30 -27
- package/dist/components/form/expressions/builder/confirmMismatchedTypesModal.js.map +1 -1
- package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.d.ts +1 -1
- package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.js +7 -5
- package/dist/components/form/expressions/builder/confirmUnsupportedTypeModal.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts +34 -2
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +170 -80
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/builder/item.d.ts +6 -2
- package/dist/components/form/expressions/builder/item.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/item.js +19 -5
- package/dist/components/form/expressions/builder/item.js.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.js +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
- package/dist/components/form/expressions/dpqlMockRecordPath.d.ts +11 -0
- package/dist/components/form/expressions/dpqlMockRecordPath.d.ts.map +1 -0
- package/dist/components/form/expressions/dpqlMockRecordPath.js +254 -0
- package/dist/components/form/expressions/dpqlMockRecordPath.js.map +1 -0
- package/dist/components/form/expressions/expressionOperands.d.ts +16 -0
- package/dist/components/form/expressions/expressionOperands.d.ts.map +1 -0
- package/dist/components/form/expressions/expressionOperands.js +97 -0
- package/dist/components/form/expressions/expressionOperands.js.map +1 -0
- package/dist/components/form/expressions/fieldPath.d.ts +24 -0
- package/dist/components/form/expressions/fieldPath.d.ts.map +1 -0
- package/dist/components/form/expressions/fieldPath.js +152 -0
- package/dist/components/form/expressions/fieldPath.js.map +1 -0
- package/dist/components/form/expressions/loneValue.d.ts +17 -0
- package/dist/components/form/expressions/loneValue.d.ts.map +1 -0
- package/dist/components/form/expressions/loneValue.js +33 -0
- package/dist/components/form/expressions/loneValue.js.map +1 -0
- package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/mockExpressions.js +22 -5
- package/dist/components/form/expressions/mockExpressions.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts +2 -0
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +37 -25
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/expressions/textOfExpression.d.ts +18 -0
- package/dist/components/form/expressions/textOfExpression.d.ts.map +1 -0
- package/dist/components/form/expressions/textOfExpression.js +49 -0
- package/dist/components/form/expressions/textOfExpression.js.map +1 -0
- package/dist/components/form/expressions/types.d.ts +12 -0
- package/dist/components/form/expressions/types.d.ts.map +1 -1
- package/dist/components/form/expressions/types.js +6 -1
- package/dist/components/form/expressions/types.js.map +1 -1
- package/dist/components/form/expressions/useExpressionType.d.ts +26 -0
- package/dist/components/form/expressions/useExpressionType.d.ts.map +1 -0
- package/dist/components/form/expressions/useExpressionType.js +181 -0
- package/dist/components/form/expressions/useExpressionType.js.map +1 -0
- package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.js +13 -9
- package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
- package/dist/components/form/expressions/wildcardExpressions.d.ts +27 -0
- package/dist/components/form/expressions/wildcardExpressions.d.ts.map +1 -0
- package/dist/components/form/expressions/wildcardExpressions.js +101 -0
- package/dist/components/form/expressions/wildcardExpressions.js.map +1 -0
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +39 -18
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +0 -7
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +46 -35
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/allowed-values/choicePicture.d.ts +37 -0
- package/dist/components/form/fields/allowed-values/choicePicture.d.ts.map +1 -0
- package/dist/components/form/fields/allowed-values/choicePicture.js +64 -0
- package/dist/components/form/fields/allowed-values/choicePicture.js.map +1 -0
- package/dist/components/form/fields/array/ArrayAuto.d.ts.map +1 -1
- package/dist/components/form/fields/array/ArrayAuto.js +24 -0
- package/dist/components/form/fields/array/ArrayAuto.js.map +1 -1
- package/dist/components/form/fields/array/ArrayAutoField.d.ts.map +1 -1
- package/dist/components/form/fields/array/ArrayAutoField.js +36 -13
- package/dist/components/form/fields/array/ArrayAutoField.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +30 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +123 -79
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/auto/hostEditorValue.d.ts +30 -0
- package/dist/components/form/fields/auto/hostEditorValue.d.ts.map +1 -0
- package/dist/components/form/fields/auto/hostEditorValue.js +53 -0
- package/dist/components/form/fields/auto/hostEditorValue.js.map +1 -0
- package/dist/components/form/fields/binary/Binary.d.ts.map +1 -1
- package/dist/components/form/fields/binary/Binary.js +23 -3
- package/dist/components/form/fields/binary/Binary.js.map +1 -1
- package/dist/components/form/fields/boolean/Boolean.d.ts +5 -0
- package/dist/components/form/fields/boolean/Boolean.d.ts.map +1 -1
- package/dist/components/form/fields/boolean/Boolean.js +9 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/color/Color.d.ts +103 -16
- package/dist/components/form/fields/color/Color.d.ts.map +1 -1
- package/dist/components/form/fields/color/Color.js +616 -16
- package/dist/components/form/fields/color/Color.js.map +1 -1
- package/dist/components/form/fields/emittedValues.d.ts +34 -3
- package/dist/components/form/fields/emittedValues.d.ts.map +1 -1
- package/dist/components/form/fields/emittedValues.js +24 -1
- package/dist/components/form/fields/emittedValues.js.map +1 -1
- package/dist/components/form/fields/file/File.d.ts +1 -1
- package/dist/components/form/fields/file/File.d.ts.map +1 -1
- package/dist/components/form/fields/file/File.js +4 -2
- package/dist/components/form/fields/file/File.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts +1 -7
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +30 -13
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/markdown/MarkdownView.js +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +10 -7
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/number/Number.d.ts +7 -4
- package/dist/components/form/fields/number/Number.d.ts.map +1 -1
- package/dist/components/form/fields/number/Number.js +45 -18
- package/dist/components/form/fields/number/Number.js.map +1 -1
- package/dist/components/form/fields/object/Object.d.ts +1 -1
- package/dist/components/form/fields/object/Object.d.ts.map +1 -1
- package/dist/components/form/fields/object/Object.js +4 -2
- package/dist/components/form/fields/object/Object.js.map +1 -1
- package/dist/components/form/fields/pendingEdit.d.ts +52 -0
- package/dist/components/form/fields/pendingEdit.d.ts.map +1 -0
- package/dist/components/form/fields/pendingEdit.js +79 -0
- package/dist/components/form/fields/pendingEdit.js.map +1 -0
- package/dist/components/form/fields/radio-group/RadioGroup.d.ts.map +1 -1
- package/dist/components/form/fields/radio-group/RadioGroup.js +8 -13
- package/dist/components/form/fields/radio-group/RadioGroup.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +20 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +426 -91
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/rich-text/useTemplateTags.d.ts +18 -0
- package/dist/components/form/fields/rich-text/useTemplateTags.d.ts.map +1 -0
- package/dist/components/form/fields/rich-text/useTemplateTags.js +80 -0
- package/dist/components/form/fields/rich-text/useTemplateTags.js.map +1 -0
- package/dist/components/form/fields/schema-definition/CatalogLeafForm.d.ts.map +1 -1
- package/dist/components/form/fields/schema-definition/CatalogLeafForm.js +4 -1
- package/dist/components/form/fields/schema-definition/CatalogLeafForm.js.map +1 -1
- package/dist/components/form/fields/schema-definition/CatalogNodeEditor.d.ts.map +1 -1
- package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js +20 -5
- package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js.map +1 -1
- package/dist/components/form/fields/schema-definition/TableEditor.d.ts.map +1 -1
- package/dist/components/form/fields/schema-definition/TableEditor.js +10 -2
- package/dist/components/form/fields/schema-definition/TableEditor.js.map +1 -1
- package/dist/components/form/fields/schema-definition/index.d.ts.map +1 -1
- package/dist/components/form/fields/schema-definition/index.js +8 -4
- package/dist/components/form/fields/schema-definition/index.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts +10 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +28 -14
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +5 -0
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +25 -12
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/fields/select/unavailableRow.d.ts +23 -0
- package/dist/components/form/fields/select/unavailableRow.d.ts.map +1 -0
- package/dist/components/form/fields/select/unavailableRow.js +55 -0
- package/dist/components/form/fields/select/unavailableRow.js.map +1 -0
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +11 -5
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +21 -5
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateBrowser.d.ts +16 -0
- package/dist/components/form/fields/template/TemplateBrowser.d.ts.map +1 -0
- package/dist/components/form/fields/template/TemplateBrowser.js +89 -0
- package/dist/components/form/fields/template/TemplateBrowser.js.map +1 -0
- package/dist/components/form/fields/template/TemplateField.d.ts +105 -1
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +706 -168
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/fields/template/TemplatePicker.d.ts +54 -0
- package/dist/components/form/fields/template/TemplatePicker.d.ts.map +1 -0
- package/dist/components/form/fields/template/TemplatePicker.js +459 -0
- package/dist/components/form/fields/template/TemplatePicker.js.map +1 -0
- package/dist/components/form/fields/template/TemplateText.d.ts +0 -17
- package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateText.js +22 -16
- package/dist/components/form/fields/template/TemplateText.js.map +1 -1
- package/dist/components/form/fields/template/referenceNames.d.ts +63 -0
- package/dist/components/form/fields/template/referenceNames.d.ts.map +1 -0
- package/dist/components/form/fields/template/referenceNames.js +69 -0
- package/dist/components/form/fields/template/referenceNames.js.map +1 -0
- package/dist/components/form/fields/template/writtenAsText.d.ts +61 -0
- package/dist/components/form/fields/template/writtenAsText.d.ts.map +1 -0
- package/dist/components/form/fields/template/writtenAsText.js +204 -0
- package/dist/components/form/fields/template/writtenAsText.js.map +1 -0
- package/dist/components/form/index.d.ts +12 -2
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +25 -3
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/typeSentenceForm.d.ts +17 -0
- package/dist/components/form/typeSentenceForm.d.ts.map +1 -0
- package/dist/components/form/typeSentenceForm.js +54 -0
- package/dist/components/form/typeSentenceForm.js.map +1 -0
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.js +19 -9
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -1
- package/dist/components/log/Log.d.ts.map +1 -1
- package/dist/components/log/Log.js +20 -8
- package/dist/components/log/Log.js.map +1 -1
- package/dist/components/menu/Menu.d.ts.map +1 -1
- package/dist/components/menu/Menu.js +19 -10
- package/dist/components/menu/Menu.js.map +1 -1
- package/dist/components/smartEditor/CompletionMenu.d.ts +53 -0
- package/dist/components/smartEditor/CompletionMenu.d.ts.map +1 -0
- package/dist/components/smartEditor/CompletionMenu.js +302 -0
- package/dist/components/smartEditor/CompletionMenu.js.map +1 -0
- package/dist/components/smartEditor/MarkdownDoc.d.ts +1 -1
- package/dist/components/smartEditor/MarkdownDoc.js +3 -3
- package/dist/components/smartEditor/MarkdownDoc.js.map +1 -1
- package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
- package/dist/components/smartEditor/SmartEditor.js +616 -190
- package/dist/components/smartEditor/SmartEditor.js.map +1 -1
- package/dist/components/smartEditor/completionStyle.d.ts +28 -0
- package/dist/components/smartEditor/completionStyle.d.ts.map +1 -0
- package/dist/components/smartEditor/completionStyle.js +79 -0
- package/dist/components/smartEditor/completionStyle.js.map +1 -0
- package/dist/components/smartEditor/contextTitle.d.ts +10 -0
- package/dist/components/smartEditor/contextTitle.d.ts.map +1 -0
- package/dist/components/smartEditor/contextTitle.js +21 -0
- package/dist/components/smartEditor/contextTitle.js.map +1 -0
- package/dist/components/smartEditor/editingAids.d.ts +64 -0
- package/dist/components/smartEditor/editingAids.d.ts.map +1 -0
- package/dist/components/smartEditor/editingAids.js +213 -0
- package/dist/components/smartEditor/editingAids.js.map +1 -0
- package/dist/components/smartEditor/index.d.ts +6 -1
- package/dist/components/smartEditor/index.d.ts.map +1 -1
- package/dist/components/smartEditor/index.js +19 -1
- package/dist/components/smartEditor/index.js.map +1 -1
- package/dist/components/smartEditor/templateCompletions.d.ts +98 -0
- package/dist/components/smartEditor/templateCompletions.d.ts.map +1 -0
- package/dist/components/smartEditor/templateCompletions.js +380 -0
- package/dist/components/smartEditor/templateCompletions.js.map +1 -0
- package/dist/components/smartEditor/typedReferences.d.ts +34 -0
- package/dist/components/smartEditor/typedReferences.d.ts.map +1 -0
- package/dist/components/smartEditor/typedReferences.js +179 -0
- package/dist/components/smartEditor/typedReferences.js.map +1 -0
- package/dist/components/smartEditor/types.d.ts +116 -2
- package/dist/components/smartEditor/types.d.ts.map +1 -1
- package/dist/components/smartEditor/useEditingAids.d.ts +38 -0
- package/dist/components/smartEditor/useEditingAids.d.ts.map +1 -0
- package/dist/components/smartEditor/useEditingAids.js +392 -0
- package/dist/components/smartEditor/useEditingAids.js.map +1 -0
- package/dist/components/smartEditor/useLspAutocomplete.d.ts +106 -3
- package/dist/components/smartEditor/useLspAutocomplete.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspAutocomplete.js +592 -149
- package/dist/components/smartEditor/useLspAutocomplete.js.map +1 -1
- package/dist/components/smartEditor/useLspHover.d.ts +33 -7
- package/dist/components/smartEditor/useLspHover.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspHover.js +364 -35
- package/dist/components/smartEditor/useLspHover.js.map +1 -1
- package/dist/components/smartEditor/useLspSemanticTokens.js +1 -1
- package/dist/components/smartEditor/useLspSemanticTokens.js.map +1 -1
- package/dist/components/smartEditor/useLspSession.d.ts +6 -0
- package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspSession.js +7 -6
- package/dist/components/smartEditor/useLspSession.js.map +1 -1
- package/dist/components/smartEditor/useLspSignatureHelp.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspSignatureHelp.js +6 -11
- package/dist/components/smartEditor/useLspSignatureHelp.js.map +1 -1
- package/dist/components/smartEditor/useTabCameIn.d.ts +10 -0
- package/dist/components/smartEditor/useTabCameIn.d.ts.map +1 -0
- package/dist/components/smartEditor/useTabCameIn.js +42 -0
- package/dist/components/smartEditor/useTabCameIn.js.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -1
- package/dist/components/supportTicket/AttachmentChips.js +15 -5
- package/dist/components/supportTicket/AttachmentChips.js.map +1 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +2 -3
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -1
- package/dist/components/supportTicket/ReferencePicker.d.ts +9 -3
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
- package/dist/components/supportTicket/ReferencePicker.js +38 -23
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
- package/dist/components/supportTicket/TicketHeader.d.ts +3 -4
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketHeader.js +21 -6
- package/dist/components/supportTicket/TicketHeader.js.map +1 -1
- package/dist/components/supportTicket/TicketMetaTags.d.ts +1 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.js +10 -29
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
- package/dist/components/supportTicket/useLoadedImages.d.ts +0 -10
- package/dist/components/supportTicket/useLoadedImages.d.ts.map +1 -1
- package/dist/components/supportTicket/useLoadedImages.js +1 -63
- package/dist/components/supportTicket/useLoadedImages.js.map +1 -1
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -1
- package/dist/components/ticketReferences/TicketReferences.js +10 -9
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -1
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
- package/dist/components/ticketThread/TicketThread.js +52 -21
- package/dist/components/ticketThread/TicketThread.js.map +1 -1
- package/dist/helpers/allowedValues.d.ts +21 -12
- package/dist/helpers/allowedValues.d.ts.map +1 -1
- package/dist/helpers/allowedValues.js +40 -20
- package/dist/helpers/allowedValues.js.map +1 -1
- package/dist/helpers/descriptionTooltip.d.ts +46 -0
- package/dist/helpers/descriptionTooltip.d.ts.map +1 -0
- package/dist/helpers/descriptionTooltip.js +76 -0
- package/dist/helpers/descriptionTooltip.js.map +1 -0
- package/dist/helpers/domProps.d.ts +5 -0
- package/dist/helpers/domProps.d.ts.map +1 -0
- package/dist/helpers/domProps.js +47 -0
- package/dist/helpers/domProps.js.map +1 -0
- package/dist/helpers/expressions.d.ts +115 -4
- package/dist/helpers/expressions.d.ts.map +1 -1
- package/dist/helpers/expressions.js +247 -29
- package/dist/helpers/expressions.js.map +1 -1
- package/dist/helpers/hoveringPointer.d.ts +26 -0
- package/dist/helpers/hoveringPointer.d.ts.map +1 -0
- package/dist/helpers/hoveringPointer.js +29 -0
- package/dist/helpers/hoveringPointer.js.map +1 -0
- package/dist/helpers/markdownText.d.ts +14 -0
- package/dist/helpers/markdownText.d.ts.map +1 -0
- package/dist/helpers/markdownText.js +160 -0
- package/dist/helpers/markdownText.js.map +1 -0
- package/dist/helpers/noDescription.d.ts +9 -0
- package/dist/helpers/noDescription.d.ts.map +1 -0
- package/dist/helpers/noDescription.js +13 -0
- package/dist/helpers/noDescription.js.map +1 -0
- package/dist/helpers/optionUiTypes.d.ts +2 -2
- package/dist/helpers/qorusTypeCatalogue.d.ts +147 -0
- package/dist/helpers/qorusTypeCatalogue.d.ts.map +1 -0
- package/dist/helpers/qorusTypeCatalogue.js +505 -0
- package/dist/helpers/qorusTypeCatalogue.js.map +1 -0
- package/dist/helpers/readableColors.d.ts +36 -0
- package/dist/helpers/readableColors.d.ts.map +1 -0
- package/dist/helpers/readableColors.js +116 -0
- package/dist/helpers/readableColors.js.map +1 -0
- package/dist/helpers/recordFieldReference.d.ts +15 -0
- package/dist/helpers/recordFieldReference.d.ts.map +1 -0
- package/dist/helpers/recordFieldReference.js +89 -0
- package/dist/helpers/recordFieldReference.js.map +1 -0
- package/dist/helpers/singleLineString.d.ts.map +1 -1
- package/dist/helpers/singleLineString.js +0 -1
- package/dist/helpers/singleLineString.js.map +1 -1
- package/dist/helpers/templateCatalogue.d.ts +104 -0
- package/dist/helpers/templateCatalogue.d.ts.map +1 -0
- package/dist/helpers/templateCatalogue.js +116 -0
- package/dist/helpers/templateCatalogue.js.map +1 -0
- package/dist/helpers/templateCatalogueContext.d.ts +19 -0
- package/dist/helpers/templateCatalogueContext.d.ts.map +1 -0
- package/dist/helpers/templateCatalogueContext.js +31 -0
- package/dist/helpers/templateCatalogueContext.js.map +1 -0
- package/dist/helpers/templateItems.d.ts +2 -2
- package/dist/helpers/templateItems.d.ts.map +1 -1
- package/dist/helpers/templateItems.js +6 -4
- package/dist/helpers/templateItems.js.map +1 -1
- package/dist/helpers/templateText.d.ts +1 -0
- package/dist/helpers/templateText.d.ts.map +1 -1
- package/dist/helpers/templateText.js +7 -3
- package/dist/helpers/templateText.js.map +1 -1
- package/dist/helpers/templateTree.d.ts +248 -0
- package/dist/helpers/templateTree.d.ts.map +1 -0
- package/dist/helpers/templateTree.js +892 -0
- package/dist/helpers/templateTree.js.map +1 -0
- package/dist/helpers/templateType.d.ts +28 -0
- package/dist/helpers/templateType.d.ts.map +1 -0
- package/dist/helpers/templateType.js +30 -0
- package/dist/helpers/templateType.js.map +1 -0
- package/dist/helpers/templates.d.ts +86 -16
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +305 -45
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts +27 -0
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +169 -39
- package/dist/helpers/validations.js.map +1 -1
- package/dist/helpers/webLink.d.ts +8 -0
- package/dist/helpers/webLink.d.ts.map +1 -0
- package/dist/helpers/webLink.js +29 -0
- package/dist/helpers/webLink.js.map +1 -0
- package/dist/hooks/useQorusTypes.d.ts +12 -17
- package/dist/hooks/useQorusTypes.d.ts.map +1 -1
- package/dist/hooks/useQorusTypes.js +44 -172
- package/dist/hooks/useQorusTypes.js.map +1 -1
- package/dist/hooks/useReadableIntentShade.d.ts +9 -0
- package/dist/hooks/useReadableIntentShade.d.ts.map +1 -0
- package/dist/hooks/useReadableIntentShade.js +23 -0
- package/dist/hooks/useReadableIntentShade.js.map +1 -0
- package/dist/hooks/useReadableOpacity.d.ts +6 -0
- package/dist/hooks/useReadableOpacity.d.ts.map +1 -0
- package/dist/hooks/useReadableOpacity.js +17 -0
- package/dist/hooks/useReadableOpacity.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/tracking/ui/ExperimentCard.d.ts +1 -1
- package/dist/tracking/ui/ExperimentCard.d.ts.map +1 -1
- package/dist/tracking/ui/ExperimentCard.js +26 -10
- package/dist/tracking/ui/ExperimentCard.js.map +1 -1
- package/dist/tracking/ui/card.d.ts +2 -2
- package/dist/tracking/ui/card.d.ts.map +1 -1
- package/dist/tracking/ui/card.js +2 -2
- package/dist/tracking/ui/card.js.map +1 -1
- package/dist/tracking/ui/readable.d.ts +14 -0
- package/dist/tracking/ui/readable.d.ts.map +1 -0
- package/dist/tracking/ui/readable.js +60 -0
- package/dist/tracking/ui/readable.js.map +1 -0
- package/dist/utils/fetch.d.ts +9 -2
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +6 -5
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/lspClient.types.d.ts +2 -0
- package/dist/utils/lspClient.types.d.ts.map +1 -1
- package/dist/utils/websocket.d.ts +65 -2
- package/dist/utils/websocket.d.ts.map +1 -1
- package/dist/utils/websocket.js +276 -44
- package/dist/utils/websocket.js.map +1 -1
- package/package.json +14 -6
- package/.claude/CLAUDE.md +0 -317
- package/.tasks/EXPORT_EXPRESSION_BUILDER.md +0 -38
- package/.tasks/EXPRESSION_BUILDER_REPORT.md +0 -149
- package/.tasks/EXPRESSION_FIELD_SEAMS.md +0 -66
- package/.tasks/FIELD_STACK_REPORT.md +0 -234
- package/.tasks/FORM_ENGINE_COMPACT_READ_FIRST.md +0 -558
- package/.tasks/FORM_ENGINE_EXPRESSIONS.md +0 -197
- package/.tasks/FORM_ENGINE_OPTIONS_SYNC.md +0 -95
- package/.tasks/FORM_FIELD_EXTRAS.md +0 -72
- package/.tasks/INDEX.md +0 -111
- package/.tasks/LSP_SHARED_CONNECTION.md +0 -75
- package/.tasks/QONSOLE_ASSIST_FEATURES.md +0 -306
- package/.tasks/RENDER_EXPRESSION_LSP.md +0 -159
- package/.tasks/REQRAFT_AUTO_FIELD.md +0 -176
- package/.tasks/SCHEMA_DEFINITION_FIELD.md +0 -250
- package/.tasks/SMART_EDITOR_CONTEXT_AND_POLISH.md +0 -340
- package/.tasks/SMART_EDITOR_LSP_FEATURES.md +0 -299
- package/.tasks/SMART_EDITOR_REVIEW_FOLLOWUPS.md +0 -280
- package/.tasks/SMART_EDITOR_UX_POLISH.md +0 -383
- package/.tasks/SMART_EDITOR_VISUAL_POLISH.md +0 -246
- package/.tasks/STORY_NETWORK_ISOLATION.md +0 -98
- package/.tasks/TEMPLATE_TOKEN_GRAMMAR.md +0 -72
- package/CODE_OF_CONDUCT.md +0 -105
- package/CONTRIBUTING.MD +0 -78
- package/SECURITY.md +0 -3
- package/chromatic.config.json +0 -5
- package/design/COMPACT_ENGINE_REDESIGN.md +0 -156
- package/design/COMPACT_INFO_DISPLAY.md +0 -87
- package/design/COMPACT_PARITY.md +0 -85
- package/design/EXPRESSION_BUILDER_REPORT_STRATEGY.md +0 -92
- package/design/FORM_ENGINE_COMPACT.md +0 -300
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +0 -353
- package/design/FORM_ENGINE_EXPRESSIONS.md +0 -257
- package/design/FORM_ENGINE_FIELD_MIGRATION.md +0 -419
- package/design/IDE_INTEGRATION.md +0 -64
- package/design/RENDER_EXPRESSION_TRANSPORT.md +0 -156
- package/design/SELECT_DIALOG_FLOOR.md +0 -86
- package/design/SMART_EDITOR_UX.md +0 -438
- package/design/TRACKING.md +0 -290
- package/design/UNAVAILABLE_ALLOWED_VALUES.md +0 -226
- package/dist/components/form/engine/formAppearance.d.ts +0 -49
- package/dist/components/form/engine/formAppearance.d.ts.map +0 -1
- package/dist/components/form/engine/formAppearance.js +0 -50
- package/dist/components/form/engine/formAppearance.js.map +0 -1
- package/dist/components/form/fields/ChoiceGroupHeading.d.ts +0 -21
- package/dist/components/form/fields/ChoiceGroupHeading.d.ts.map +0 -1
- package/dist/components/form/fields/ChoiceGroupHeading.js +0 -33
- package/dist/components/form/fields/ChoiceGroupHeading.js.map +0 -1
- package/dist/components/form/fields/allowed-values/savedValues.d.ts +0 -30
- package/dist/components/form/fields/allowed-values/savedValues.d.ts.map +0 -1
- package/dist/components/form/fields/allowed-values/savedValues.js +0 -39
- package/dist/components/form/fields/allowed-values/savedValues.js.map +0 -1
- package/dist/helpers/fieldDescription.d.ts +0 -16
- package/dist/helpers/fieldDescription.d.ts.map +0 -1
- package/dist/helpers/fieldDescription.js +0 -40
- package/dist/helpers/fieldDescription.js.map +0 -1
- package/src/components/Description/index.tsx +0 -254
- package/src/components/Description/markdownRendererContext.tsx +0 -29
- package/src/components/FocusedEditing/index.tsx +0 -50
- package/src/components/codeSize/index.tsx +0 -79
- package/src/components/composer/Composer.stories.tsx +0 -238
- package/src/components/composer/Composer.tsx +0 -554
- package/src/components/composer/helpers.ts +0 -74
- package/src/components/composer/index.ts +0 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +0 -1183
- package/src/components/dpqlEditor/DpqlEditor.tsx +0 -219
- package/src/components/dpqlEditor/dpqlHelpers.ts +0 -287
- package/src/components/dpqlEditor/dpqlInserter.ts +0 -121
- package/src/components/dpqlEditor/dpqlProbe.ts +0 -165
- package/src/components/dpqlEditor/dpqlTags.ts +0 -114
- package/src/components/dpqlEditor/index.ts +0 -41
- package/src/components/dpqlEditor/types.ts +0 -203
- package/src/components/dpqlEditor/useDpqlProbe.ts +0 -33
- package/src/components/dpqlEditor/useDpqlSession.ts +0 -411
- package/src/components/form/engine/CompactRow.tsx +0 -2740
- package/src/components/form/engine/CompactToolbar.tsx +0 -399
- package/src/components/form/engine/FormEngine.stories.tsx +0 -9934
- package/src/components/form/engine/FormEngine.tsx +0 -4891
- package/src/components/form/engine/FormEngineAppearance.stories.tsx +0 -609
- package/src/components/form/engine/FormEngineRemote.stories.tsx +0 -679
- package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +0 -335
- package/src/components/form/engine/FormFieldsSkeleton.tsx +0 -82
- package/src/components/form/engine/OptionFieldMessages.tsx +0 -548
- package/src/components/form/engine/OptionsHelpDialog.tsx +0 -29
- package/src/components/form/engine/__fixtures__/basicFormOptions.ts +0 -309
- package/src/components/form/engine/__fixtures__/fieldChromeOptions.ts +0 -45
- package/src/components/form/engine/_structuredData/SchemaDataView.stories.tsx +0 -233
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +0 -755
- package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +0 -85
- package/src/components/form/engine/_structuredData/StructuredDataView.tsx +0 -75
- package/src/components/form/engine/_structuredData/structuredData.ts +0 -145
- package/src/components/form/engine/compactRowContext.ts +0 -153
- package/src/components/form/engine/compactRowStyles.ts +0 -1087
- package/src/components/form/engine/compactToolbarContext.ts +0 -72
- package/src/components/form/engine/formAppearance.ts +0 -79
- package/src/components/form/engine/optionActions.ts +0 -128
- package/src/components/form/engine/optionsContext.ts +0 -24
- package/src/components/form/engine/readFirst.ts +0 -1090
- package/src/components/form/engine/rendererTypes.ts +0 -107
- package/src/components/form/engine/revealRow.ts +0 -123
- package/src/components/form/engine/rowFocus.ts +0 -112
- package/src/components/form/engine/rowMenuContext.ts +0 -159
- package/src/components/form/engine/rowOpenPicker.ts +0 -27
- package/src/components/form/engine/typeChoices.ts +0 -63
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +0 -159
- package/src/components/form/engine/variants/VariantCalmTable.tsx +0 -244
- package/src/components/form/engine/variants/VariantCards.tsx +0 -212
- package/src/components/form/engine/variants/VariantFocus.tsx +0 -382
- package/src/components/form/engine/variants/VariantMinimal.tsx +0 -170
- package/src/components/form/engine/variants/focusDemo.ts +0 -145
- package/src/components/form/engine/variants/variantModel.ts +0 -223
- package/src/components/form/engine/variants/variantParts.tsx +0 -313
- package/src/components/form/expressions/DpqlRendering.tsx +0 -85
- package/src/components/form/expressions/ExpressionField.stories.tsx +0 -1395
- package/src/components/form/expressions/ExpressionField.tsx +0 -503
- package/src/components/form/expressions/argumentPresence.ts +0 -213
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +0 -1683
- package/src/components/form/expressions/builder/argumentDetail.tsx +0 -17
- package/src/components/form/expressions/builder/argumentLabel.tsx +0 -146
- package/src/components/form/expressions/builder/argumentSlot.tsx +0 -101
- package/src/components/form/expressions/builder/argumentWrapper.tsx +0 -243
- package/src/components/form/expressions/builder/confirmMismatchedTypesModal.tsx +0 -120
- package/src/components/form/expressions/builder/confirmUnsupportedTypeModal.tsx +0 -69
- package/src/components/form/expressions/builder/index.tsx +0 -1545
- package/src/components/form/expressions/builder/item.tsx +0 -89
- package/src/components/form/expressions/builder/moveToPositionStrip.tsx +0 -156
- package/src/components/form/expressions/builder/renderTemplate.tsx +0 -42
- package/src/components/form/expressions/dpqlMockLanguage.ts +0 -566
- package/src/components/form/expressions/dpqlMockLsp.ts +0 -126
- package/src/components/form/expressions/mockExpressions.ts +0 -317
- package/src/components/form/expressions/renderExpressionToText.ts +0 -152
- package/src/components/form/expressions/types.ts +0 -110
- package/src/components/form/expressions/useExpressions.ts +0 -101
- package/src/components/form/expressions/useRenderExpression.ts +0 -165
- package/src/components/form/fields/ChoiceGroupHeading.tsx +0 -38
- package/src/components/form/fields/Field.stories.tsx +0 -143
- package/src/components/form/fields/Field.tsx +0 -734
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +0 -321
- package/src/components/form/fields/allowed-values/savedValues.ts +0 -64
- package/src/components/form/fields/array/ArrayAuto.tsx +0 -237
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +0 -475
- package/src/components/form/fields/array/ArrayAutoField.tsx +0 -367
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +0 -825
- package/src/components/form/fields/auto/AutoFormField.tsx +0 -1183
- package/src/components/form/fields/binary/Binary.stories.tsx +0 -122
- package/src/components/form/fields/binary/Binary.tsx +0 -91
- package/src/components/form/fields/boolean/Boolean.stories.tsx +0 -125
- package/src/components/form/fields/boolean/Boolean.tsx +0 -34
- package/src/components/form/fields/byte-size/ByteSize.stories.tsx +0 -87
- package/src/components/form/fields/byte-size/ByteSize.tsx +0 -58
- package/src/components/form/fields/color/Color.stories.tsx +0 -99
- package/src/components/form/fields/color/Color.tsx +0 -46
- package/src/components/form/fields/cron/Cron.stories.tsx +0 -68
- package/src/components/form/fields/cron/Cron.tsx +0 -80
- package/src/components/form/fields/date/Date.stories.tsx +0 -77
- package/src/components/form/fields/date/Date.tsx +0 -55
- package/src/components/form/fields/emittedValues.ts +0 -70
- package/src/components/form/fields/file/File.stories.tsx +0 -161
- package/src/components/form/fields/file/File.tsx +0 -227
- package/src/components/form/fields/long-string/LongString.stories.tsx +0 -147
- package/src/components/form/fields/long-string/LongString.tsx +0 -141
- package/src/components/form/fields/markdown/Markdown.stories.tsx +0 -76
- package/src/components/form/fields/markdown/Markdown.tsx +0 -81
- package/src/components/form/fields/markdown/MarkdownView.tsx +0 -222
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +0 -140
- package/src/components/form/fields/number/Number.stories.tsx +0 -159
- package/src/components/form/fields/number/Number.tsx +0 -175
- package/src/components/form/fields/object/Object.stories.tsx +0 -398
- package/src/components/form/fields/object/Object.tsx +0 -255
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +0 -95
- package/src/components/form/fields/radio-group/RadioGroup.tsx +0 -52
- package/src/components/form/fields/radio-group/images/java-96x128.png +0 -0
- package/src/components/form/fields/radio-group/images/python-129x128.png +0 -0
- package/src/components/form/fields/radio-group/images/qore-106x128.png +0 -0
- package/src/components/form/fields/rich-text/RichText.stories.tsx +0 -260
- package/src/components/form/fields/rich-text/RichText.tsx +0 -537
- package/src/components/form/fields/schema-definition/CatalogLeafForm.tsx +0 -242
- package/src/components/form/fields/schema-definition/CatalogNodeEditor.tsx +0 -758
- package/src/components/form/fields/schema-definition/Hint.tsx +0 -22
- package/src/components/form/fields/schema-definition/InlineValidationBanner.tsx +0 -138
- package/src/components/form/fields/schema-definition/Loader.tsx +0 -20
- package/src/components/form/fields/schema-definition/MigrationsTab.tsx +0 -187
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +0 -286
- package/src/components/form/fields/schema-definition/TableEditor.tsx +0 -155
- package/src/components/form/fields/schema-definition/TablesTab.tsx +0 -138
- package/src/components/form/fields/schema-definition/catalog.ts +0 -237
- package/src/components/form/fields/schema-definition/helpers.ts +0 -90
- package/src/components/form/fields/schema-definition/index.tsx +0 -418
- package/src/components/form/fields/schema-definition/mockCatalog.ts +0 -325
- package/src/components/form/fields/schema-definition/mockDefinition.ts +0 -111
- package/src/components/form/fields/schema-definition/referenceSuggestions.tsx +0 -152
- package/src/components/form/fields/schema-definition/starterTemplate.ts +0 -68
- package/src/components/form/fields/schema-definition/types.ts +0 -185
- package/src/components/form/fields/schema-definition/useSchemaOptionCatalog.ts +0 -49
- package/src/components/form/fields/select/Select.stories.tsx +0 -872
- package/src/components/form/fields/select/Select.tsx +0 -624
- package/src/components/form/fields/select/SelectCollection.tsx +0 -594
- package/src/components/form/fields/string/String.stories.tsx +0 -86
- package/src/components/form/fields/string/String.tsx +0 -43
- package/src/components/form/fields/template/ExampleValueModal.stories.tsx +0 -114
- package/src/components/form/fields/template/ExampleValueModal.tsx +0 -99
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +0 -47
- package/src/components/form/fields/template/TemplateField.stories.tsx +0 -1121
- package/src/components/form/fields/template/TemplateField.tsx +0 -1749
- package/src/components/form/fields/template/TemplateText.tsx +0 -115
- package/src/components/form/fields/template/__fixtures__/longExampleValue.ts +0 -7
- package/src/components/form/fields/template/__fixtures__/multiLevelTemplates.json +0 -147
- package/src/components/form/fields/template/__fixtures__/templates.json +0 -83
- package/src/components/form/fields/timeout/Timeout.stories.tsx +0 -228
- package/src/components/form/fields/timeout/Timeout.tsx +0 -87
- package/src/components/form/fields/url/Url.stories.tsx +0 -94
- package/src/components/form/fields/url/Url.tsx +0 -85
- package/src/components/form/index.tsx +0 -49
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +0 -238
- package/src/components/imageLightbox/ImageLightbox.tsx +0 -238
- package/src/components/imageLightbox/index.ts +0 -1
- package/src/components/log/Log.stories.tsx +0 -333
- package/src/components/log/Log.tsx +0 -285
- package/src/components/menu/Menu.stories.tsx +0 -155
- package/src/components/menu/Menu.tsx +0 -312
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +0 -797
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.tsx +0 -105
- package/src/components/qonsoleSmartInput/index.ts +0 -16
- package/src/components/qonsoleSmartInput/qonsoleInserter.ts +0 -77
- package/src/components/qonsoleSmartInput/types.ts +0 -133
- package/src/components/qonsoleSmartInput/useQonsoleSession.ts +0 -124
- package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +0 -62
- package/src/components/searchFilterBar/SearchFilterBar.tsx +0 -144
- package/src/components/searchFilterBar/index.ts +0 -1
- package/src/components/smartEditor/MarkdownDoc.tsx +0 -190
- package/src/components/smartEditor/SmartEditor.stories.tsx +0 -475
- package/src/components/smartEditor/SmartEditor.tsx +0 -808
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +0 -205
- package/src/components/smartEditor/helpers.ts +0 -217
- package/src/components/smartEditor/index.ts +0 -35
- package/src/components/smartEditor/styling.ts +0 -45
- package/src/components/smartEditor/types.ts +0 -211
- package/src/components/smartEditor/useLspAutocomplete.ts +0 -758
- package/src/components/smartEditor/useLspDiagnosticDecorations.ts +0 -125
- package/src/components/smartEditor/useLspHover.ts +0 -230
- package/src/components/smartEditor/useLspSemanticTokens.ts +0 -298
- package/src/components/smartEditor/useLspSession.ts +0 -231
- package/src/components/smartEditor/useLspSignatureHelp.ts +0 -198
- package/src/components/supportTicket/AttachmentChips.stories.tsx +0 -171
- package/src/components/supportTicket/AttachmentChips.tsx +0 -147
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -257
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +0 -130
- package/src/components/supportTicket/ReferencePicker.stories.tsx +0 -580
- package/src/components/supportTicket/ReferencePicker.tsx +0 -573
- package/src/components/supportTicket/TicketConversation.stories.tsx +0 -120
- package/src/components/supportTicket/TicketHeader.stories.tsx +0 -183
- package/src/components/supportTicket/TicketHeader.tsx +0 -248
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +0 -103
- package/src/components/supportTicket/TicketMetaTags.tsx +0 -85
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +0 -357
- package/src/components/supportTicket/TicketReplyBox.tsx +0 -172
- package/src/components/supportTicket/__fixtures__/kindMarks.ts +0 -28
- package/src/components/supportTicket/attachmentAccess.ts +0 -97
- package/src/components/supportTicket/fileToAttachment.ts +0 -24
- package/src/components/supportTicket/index.ts +0 -10
- package/src/components/supportTicket/meta.ts +0 -206
- package/src/components/supportTicket/ticketModel.ts +0 -131
- package/src/components/supportTicket/useLoadedImages.ts +0 -93
- package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -341
- package/src/components/ticketReferences/TicketReferences.tsx +0 -914
- package/src/components/ticketReferences/index.ts +0 -2
- package/src/components/ticketThread/TicketThread.stories.tsx +0 -898
- package/src/components/ticketThread/TicketThread.tsx +0 -706
- package/src/components/ticketThread/index.ts +0 -6
- package/src/contexts/FetchContext.tsx +0 -25
- package/src/contexts/ReqraftContext.tsx +0 -9
- package/src/contexts/StorageContext.tsx +0 -33
- package/src/global.d.ts +0 -4
- package/src/helpers/allowedValues.ts +0 -268
- package/src/helpers/choiceChip.ts +0 -227
- package/src/helpers/common.ts +0 -326
- package/src/helpers/dpqlDetection.ts +0 -209
- package/src/helpers/expressions.ts +0 -140
- package/src/helpers/fieldDescription.ts +0 -39
- package/src/helpers/optionUiTypes.ts +0 -199
- package/src/helpers/options.ts +0 -150
- package/src/helpers/providerValue.ts +0 -125
- package/src/helpers/scrollbar.ts +0 -32
- package/src/helpers/singleLineString.ts +0 -62
- package/src/helpers/templateItems.ts +0 -81
- package/src/helpers/templateText.ts +0 -350
- package/src/helpers/templates.ts +0 -602
- package/src/helpers/validations.ts +0 -2085
- package/src/hooks/useArgSchema.ts +0 -37
- package/src/hooks/useCoarsePointer.ts +0 -30
- package/src/hooks/useFetch/useFetch.stories.tsx +0 -379
- package/src/hooks/useFetch/useFetch.tsx +0 -154
- package/src/hooks/usePhoneViewport.ts +0 -34
- package/src/hooks/useQorusTypes.ts +0 -233
- package/src/hooks/useReqraftProperty.ts +0 -16
- package/src/hooks/useStorage/useStorage.stories.tsx +0 -168
- package/src/hooks/useStorage/useStorage.ts +0 -35
- package/src/hooks/useTemplates.ts +0 -123
- package/src/hooks/useValidation.ts +0 -9
- package/src/hooks/useWebSocket/useWebSocket.ts +0 -224
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +0 -640
- package/src/hooks/useWhyDidYouUpdate.ts +0 -38
- package/src/index.tsx +0 -86
- package/src/providers/FetchProvider.tsx +0 -40
- package/src/providers/ReqraftProvider.tsx +0 -52
- package/src/providers/StorageProvider.tsx +0 -131
- package/src/stores/currentUser/currentUser.stories.tsx +0 -100
- package/src/stores/currentUser/currentUser.tsx +0 -94
- package/src/stories/Data/fileFieldArgSchema.json +0 -86
- package/src/stories/Tests/utils.ts +0 -320
- package/src/stories/storyNetwork.ts +0 -92
- package/src/stories/tracking.tsx +0 -206
- package/src/tracking/browser/attach.ts +0 -186
- package/src/tracking/browser/env.ts +0 -78
- package/src/tracking/core/consent.ts +0 -37
- package/src/tracking/core/engagement.ts +0 -47
- package/src/tracking/core/experiments.ts +0 -258
- package/src/tracking/core/hash.ts +0 -26
- package/src/tracking/core/ids.ts +0 -25
- package/src/tracking/core/observable.ts +0 -23
- package/src/tracking/core/queue.ts +0 -131
- package/src/tracking/core/sections.ts +0 -166
- package/src/tracking/core/storage.ts +0 -69
- package/src/tracking/core/tracker.ts +0 -417
- package/src/tracking/core/types.ts +0 -162
- package/src/tracking/core/utm.ts +0 -23
- package/src/tracking/index.ts +0 -72
- package/src/tracking/markup.ts +0 -17
- package/src/tracking/react/Experiment.tsx +0 -44
- package/src/tracking/react/TrackingProvider.tsx +0 -56
- package/src/tracking/react/hooks.ts +0 -106
- package/src/tracking/react/index.ts +0 -13
- package/src/tracking/testing.ts +0 -108
- package/src/tracking/ui/ExperimentCard.stories.tsx +0 -296
- package/src/tracking/ui/ExperimentCard.tsx +0 -667
- package/src/tracking/ui/ExperimentsOverlay.stories.tsx +0 -155
- package/src/tracking/ui/ExperimentsOverlay.tsx +0 -437
- package/src/tracking/ui/anchor.ts +0 -180
- package/src/tracking/ui/card.ts +0 -464
- package/src/tracking/ui/client.ts +0 -117
- package/src/tracking/ui/index.ts +0 -77
- package/src/tracking/ui/types.ts +0 -110
- package/src/tracking/ui/useExperimentsAdmin.ts +0 -103
- package/src/types/Form.ts +0 -29
- package/src/types.ts +0 -14
- package/src/utils/datetime.ts +0 -10
- package/src/utils/fetch.ts +0 -457
- package/src/utils/lspClient.ts +0 -711
- package/src/utils/lspClient.types.ts +0 -211
- package/src/utils/queryUnlessCancelled.ts +0 -28
- package/src/utils/websocket.ts +0 -293
- package/vite.config.ts +0 -58
- package/vitest.config.ts +0 -69
|
@@ -1,2740 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ReqoreButton,
|
|
3
|
-
ReqoreCollapsibleContent,
|
|
4
|
-
ReqoreControlGroup,
|
|
5
|
-
ReqoreDropdown,
|
|
6
|
-
ReqoreIcon,
|
|
7
|
-
ReqoreMessage,
|
|
8
|
-
ReqoreP,
|
|
9
|
-
ReqoreTag,
|
|
10
|
-
ReqoreTagGroup,
|
|
11
|
-
} from '@qoretechnologies/reqore';
|
|
12
|
-
import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
13
|
-
import { RowMenuContext, useRowMenuRegistry } from './rowMenuContext';
|
|
14
|
-
import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
|
|
15
|
-
import { optionActionAsMenuItem, resolveOptionActions, splitOptionActions } from './optionActions';
|
|
16
|
-
import {
|
|
17
|
-
IQorusFormField,
|
|
18
|
-
IQorusFormSchema,
|
|
19
|
-
TQorusForm,
|
|
20
|
-
TQorusFormFieldSchema,
|
|
21
|
-
} from '@qoretechnologies/ts-toolkit';
|
|
22
|
-
import flatten from 'lodash/flatten';
|
|
23
|
-
import isEqual from 'lodash/isEqual';
|
|
24
|
-
import size from 'lodash/size';
|
|
25
|
-
import React, { memo } from 'react';
|
|
26
|
-
import { useContextSelector } from 'use-context-selector';
|
|
27
|
-
import { hasAllDependenciesFullfilled, parseDependency } from '../../../helpers/validations';
|
|
28
|
-
import { findTemplate, isValueTemplate } from '../../../helpers/templates';
|
|
29
|
-
import { getDefaultValue, richtextToSegments, richtextToString } from '../../../helpers/common';
|
|
30
|
-
import { fieldHasLongDescription } from '../../../helpers/fieldDescription';
|
|
31
|
-
import { hasTemplateText, templateTextSegments } from '../../../helpers/templateText';
|
|
32
|
-
import { ReadOnlyTemplateTag } from '../fields/template/ReadOnlyTemplateTag';
|
|
33
|
-
import { TemplateText } from '../fields/template/TemplateText';
|
|
34
|
-
import { describeCodeSize, formatCodeChars, formatCodeLines } from '../../codeSize';
|
|
35
|
-
import { Description } from '../../Description';
|
|
36
|
-
import { useMarkdownRenderer } from '../../Description/markdownRendererContext';
|
|
37
|
-
import { FocusedEditing } from '../../FocusedEditing';
|
|
38
|
-
import { CompactRowContext } from './compactRowContext';
|
|
39
|
-
import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL, useIsCalmForm } from './formAppearance';
|
|
40
|
-
import { revealRow } from './revealRow';
|
|
41
|
-
import {
|
|
42
|
-
StyledActionSlot,
|
|
43
|
-
StyledCardHeading,
|
|
44
|
-
StyledCardLabel,
|
|
45
|
-
StyledCodePreview,
|
|
46
|
-
StyledColorSwatch,
|
|
47
|
-
StyledColumn,
|
|
48
|
-
StyledEditCard,
|
|
49
|
-
StyledLabelBlock,
|
|
50
|
-
StyledAbsorbedField,
|
|
51
|
-
StyledLabelDesc,
|
|
52
|
-
StyledRowActions,
|
|
53
|
-
StyledRowInset,
|
|
54
|
-
StyledRowLabel,
|
|
55
|
-
StyledRowValue,
|
|
56
|
-
StyledStatusDot,
|
|
57
|
-
} from './compactRowStyles';
|
|
58
|
-
import { getShownSchemaMessages, getOptionFieldMessages } from './OptionFieldMessages';
|
|
59
|
-
import { findRowFocusTarget, isClosedPickerTrigger } from './rowFocus';
|
|
60
|
-
import { RowOpenPickerContext } from './rowOpenPicker';
|
|
61
|
-
import { SchemaDataView, canRenderWithSchema } from './_structuredData/SchemaDataView';
|
|
62
|
-
import { queryUnlessCancelled } from '../../../utils/queryUnlessCancelled';
|
|
63
|
-
import {
|
|
64
|
-
colorToCss,
|
|
65
|
-
findAllowedValueOption,
|
|
66
|
-
formatBytes,
|
|
67
|
-
formatOptionValue,
|
|
68
|
-
getAllowedValueIcon,
|
|
69
|
-
getAllowedValueImage,
|
|
70
|
-
getFileSize,
|
|
71
|
-
getHashEntries,
|
|
72
|
-
getReadFirstStatus,
|
|
73
|
-
getValueType,
|
|
74
|
-
isFixedCompactAllowedValueOption,
|
|
75
|
-
isOptionValueEmpty,
|
|
76
|
-
optionHasImages,
|
|
77
|
-
getExpressionAst,
|
|
78
|
-
} from './readFirst';
|
|
79
|
-
import { StructuredDataView } from './_structuredData/StructuredDataView';
|
|
80
|
-
import { DpqlRendering } from '../expressions/DpqlRendering';
|
|
81
|
-
import { TFieldWithOwnTemplates } from './rendererTypes';
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Does this markdown value have more document in it than one row can show?
|
|
85
|
-
*
|
|
86
|
-
* Blank trailing lines do not count: a textarea leaves them behind constantly,
|
|
87
|
-
* and a one-line description that gained a stray newline must not suddenly
|
|
88
|
-
* grow an inset.
|
|
89
|
-
*/
|
|
90
|
-
const isMultilineMarkdown = (value: string): boolean => value.trim().includes('\n');
|
|
91
|
-
|
|
92
|
-
// Types that are not hashes by declaration but hold a plain object as their
|
|
93
|
-
// value, and so get the hash row's structured preview under their summary.
|
|
94
|
-
// Kept explicit: a `schema` or `richtext` value is an object too, and each of
|
|
95
|
-
// those already has a preview of its own.
|
|
96
|
-
const OBJECT_VALUED_PREVIEW_TYPES = new Set([
|
|
97
|
-
'data-provider',
|
|
98
|
-
'options',
|
|
99
|
-
'system-options',
|
|
100
|
-
'processor-mappings',
|
|
101
|
-
]);
|
|
102
|
-
|
|
103
|
-
// Types whose editors are too tall/nested to edit in-row — these keep the edit
|
|
104
|
-
// card; `arg_schema` and operator fields are excluded separately.
|
|
105
|
-
const COMPACT_COMPLEX_TYPES = new Set([
|
|
106
|
-
'hash',
|
|
107
|
-
'free-hash',
|
|
108
|
-
'list',
|
|
109
|
-
'free-list',
|
|
110
|
-
'array',
|
|
111
|
-
'array-auto',
|
|
112
|
-
'file',
|
|
113
|
-
'file-string',
|
|
114
|
-
'richtext',
|
|
115
|
-
'long-string',
|
|
116
|
-
'any',
|
|
117
|
-
'auto',
|
|
118
|
-
'schema',
|
|
119
|
-
'schema-definition',
|
|
120
|
-
'data-provider',
|
|
121
|
-
'processor-mappings',
|
|
122
|
-
'options',
|
|
123
|
-
'system-options',
|
|
124
|
-
'byte-size',
|
|
125
|
-
'timeout',
|
|
126
|
-
'markdown',
|
|
127
|
-
'method-name',
|
|
128
|
-
'class-connectors',
|
|
129
|
-
'class-array',
|
|
130
|
-
'yaml',
|
|
131
|
-
]);
|
|
132
|
-
|
|
133
|
-
// Card editors that hold a SINGLE value through one text-bearing input — their
|
|
134
|
-
// built-in ReqoreInput clear duplicates the card's own "Clear value" action, so
|
|
135
|
-
// we suppress it (the card trash action is the single source). Structural/multi-input
|
|
136
|
-
// editors (hash, list, schema…) and polymorphic ones (auto/any) are NOT listed:
|
|
137
|
-
// their per-sub-field clears are distinct affordances and must stay, while the
|
|
138
|
-
// card ✕ clears the whole value. (Operators force a card on a scalar value, so
|
|
139
|
-
// 'string' covers them — and `expr`, which is ui_type 'string'.)
|
|
140
|
-
const COMPACT_SINGLE_VALUE_TYPES = new Set([
|
|
141
|
-
'string',
|
|
142
|
-
'long-string',
|
|
143
|
-
'markdown',
|
|
144
|
-
'richtext',
|
|
145
|
-
'byte-size',
|
|
146
|
-
'timeout',
|
|
147
|
-
'method-name',
|
|
148
|
-
]);
|
|
149
|
-
|
|
150
|
-
// One read-first row: label | value | action collapsed; the real editor (the
|
|
151
|
-
// classic renderOption) expanded. `hidden` = search-surfaced optional —
|
|
152
|
-
// activating the row adds the field first.
|
|
153
|
-
/**
|
|
154
|
-
* The text inside a rich-text envelope, when a list item is one.
|
|
155
|
-
*
|
|
156
|
-
* An option like Gmail's `to` is `type: "list"`, `element_type: "string"`,
|
|
157
|
-
* `supports_templates: true`: each element is a plain string that may carry
|
|
158
|
-
* template tags, so its editor is a rich-text one and the element value arrives
|
|
159
|
-
* wrapped as `{type: "richtext", value: [{type: "paragraph", …}]}`.
|
|
160
|
-
*
|
|
161
|
-
* That envelope is a string in a coat, not structured data. The hash-list test
|
|
162
|
-
* unwraps `item.value`, found the Slate document — an object — and classed a
|
|
163
|
-
* list of email addresses as a list of hashes, so the row drew an expandable
|
|
164
|
-
* `type / children / text` tree instead of the address the operator typed.
|
|
165
|
-
*/
|
|
166
|
-
export const richtextItemText = (item: unknown): string | undefined =>
|
|
167
|
-
item && typeof item === 'object' && (item as { type?: unknown }).type === 'richtext' ?
|
|
168
|
-
richtextToString((item as { value?: Parameters<typeof richtextToString>[0] }).value)
|
|
169
|
-
: undefined;
|
|
170
|
-
|
|
171
|
-
export const CompactRow = memo(
|
|
172
|
-
({
|
|
173
|
-
optionName,
|
|
174
|
-
optionField,
|
|
175
|
-
hidden = false,
|
|
176
|
-
clustered = false,
|
|
177
|
-
clusterFirst = false,
|
|
178
|
-
clusterLast = false,
|
|
179
|
-
absorbedFields,
|
|
180
|
-
}: {
|
|
181
|
-
optionName: string;
|
|
182
|
-
optionField: IQorusFormField;
|
|
183
|
-
hidden?: boolean;
|
|
184
|
-
/** Siblings this row renders inside its own container instead of leaving
|
|
185
|
-
* them a row each — declared by the schema's `absorb_fields`. The pair it
|
|
186
|
-
* exists for is a code editor and its language: one decision, so one
|
|
187
|
-
* element. */
|
|
188
|
-
absorbedFields?: string[];
|
|
189
|
-
// Rendered inside a required-group cluster: leading status node + rail, and no
|
|
190
|
-
// per-row "one of" chip (the cluster header carries it). first/last trim the
|
|
191
|
-
// rail segment so it spans node-to-node, not past the end members.
|
|
192
|
-
clustered?: boolean;
|
|
193
|
-
clusterFirst?: boolean;
|
|
194
|
-
clusterLast?: boolean;
|
|
195
|
-
}) => {
|
|
196
|
-
const readOnly = useContextSelector(CompactRowContext, (v) => v.readOnly);
|
|
197
|
-
// The look only — see `TFormEngineAppearance`. Nothing a row DOES reads it.
|
|
198
|
-
const calm = useIsCalmForm();
|
|
199
|
-
const commitMode = useContextSelector(CompactRowContext, (v) => v.commitMode);
|
|
200
|
-
const options = useContextSelector(CompactRowContext, (v) => v.options);
|
|
201
|
-
// Whether this field's emptiness is a fault yet: the person has opened it and left,
|
|
202
|
-
// or the host reported a submit attempt. Before that a required field is only to
|
|
203
|
-
// fill in, and says so in the quiet colour.
|
|
204
|
-
const visited = useContextSelector(
|
|
205
|
-
CompactRowContext,
|
|
206
|
-
(v) => v.revealRequired || !!v.visitedOptions?.includes(optionName)
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
// An absorbed field keeps its own name — it is a different field, not a
|
|
210
|
-
// property of its host — so the label comes from its schema exactly as it
|
|
211
|
-
// would on its own row.
|
|
212
|
-
const getAbsorbedLabel = (absorbedName: string): string =>
|
|
213
|
-
(options?.[absorbedName] as { display_name?: string } | undefined)?.display_name ||
|
|
214
|
-
absorbedName;
|
|
215
|
-
const codePreviewRenderer = useContextSelector(CompactRowContext, (v) => v.codePreviewRenderer);
|
|
216
|
-
const markdownRenderer = useMarkdownRenderer();
|
|
217
|
-
const operators = useContextSelector(CompactRowContext, (v) => v.operators);
|
|
218
|
-
const focusedEditing = useContextSelector(CompactRowContext, (v) => v.focusedEditing);
|
|
219
|
-
const showFieldTypes = useContextSelector(CompactRowContext, (v) => v.showFieldTypes);
|
|
220
|
-
const showAllDescriptions = useContextSelector(CompactRowContext, (v) => v.showAllDescriptions);
|
|
221
|
-
const isExpanded = useContextSelector(CompactRowContext, (v) =>
|
|
222
|
-
v.expandedOptions.includes(optionName)
|
|
223
|
-
);
|
|
224
|
-
const isHighlighted = useContextSelector(CompactRowContext, (v) =>
|
|
225
|
-
v.highlightedOptions.includes(optionName)
|
|
226
|
-
);
|
|
227
|
-
const isFlashed = useContextSelector(CompactRowContext, (v) =>
|
|
228
|
-
v.flashedOptions.includes(optionName)
|
|
229
|
-
);
|
|
230
|
-
const setHighlightedOptions = useContextSelector(
|
|
231
|
-
CompactRowContext,
|
|
232
|
-
(v) => v.setHighlightedOptions
|
|
233
|
-
);
|
|
234
|
-
const setFocusedEditing = useContextSelector(CompactRowContext, (v) => v.setFocusedEditing);
|
|
235
|
-
const readRowHeights = useContextSelector(CompactRowContext, (v) => v.readRowHeights);
|
|
236
|
-
const onReadOnlyActivate = useContextSelector(
|
|
237
|
-
CompactRowContext,
|
|
238
|
-
(v) => v.onReadOnlyActivate
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* The floor recorded when a row is opened lasts until the author changes
|
|
243
|
-
* something.
|
|
244
|
-
*
|
|
245
|
-
* `activate()` measures the row as it was READ and pins that as the
|
|
246
|
-
* editing row's `min-height`, so opening a row cannot yank the content
|
|
247
|
-
* below it up under the pointer. That is right for the transition and
|
|
248
|
-
* wrong for the rest of the session: a row that was tall because it
|
|
249
|
-
* carried a warning keeps that height after the author fixes the very
|
|
250
|
-
* thing the warning was about, leaving a large empty box where the
|
|
251
|
-
* explanation used to be — reported as "the chip is removed, but the
|
|
252
|
-
* vertical size of the option remains the same".
|
|
253
|
-
*
|
|
254
|
-
* Released on the first change to the value, which is exactly the moment
|
|
255
|
-
* the author has acted and the row should settle to what it now holds.
|
|
256
|
-
* Nothing is yanked by that: the author caused it and is looking at it.
|
|
257
|
-
*
|
|
258
|
-
* Keyed on a STRING rather than the value itself. A form value is often a
|
|
259
|
-
* fresh object on every render (`{source, value}` for a reference), so a
|
|
260
|
-
* dependency on its identity would drop the floor on the first re-render
|
|
261
|
-
* and the transition it exists for would never be smoothed at all.
|
|
262
|
-
*/
|
|
263
|
-
const valueKey =
|
|
264
|
-
optionField?.value === null || typeof optionField?.value !== 'object' ?
|
|
265
|
-
String(optionField?.value)
|
|
266
|
-
: JSON.stringify(optionField?.value);
|
|
267
|
-
|
|
268
|
-
/* `valueKey` alone, deliberately - see above. No eslint-disable: this repo
|
|
269
|
-
does not load `react-hooks`, so a suppression for a rule that is not
|
|
270
|
-
there is itself an error, and the pre-push lint refuses the push. */
|
|
271
|
-
React.useEffect(() => {
|
|
272
|
-
delete readRowHeights.current[optionName];
|
|
273
|
-
}, [valueKey]);
|
|
274
|
-
const originalValue = useContextSelector(CompactRowContext, (v) => v.originalValue);
|
|
275
|
-
const availableOptions = useContextSelector(CompactRowContext, (v) => v.availableOptions);
|
|
276
|
-
const requiredGroupsInfo = useContextSelector(CompactRowContext, (v) => v.requiredGroupsInfo);
|
|
277
|
-
const handleValueChange = useContextSelector(CompactRowContext, (v) => v.handleValueChange);
|
|
278
|
-
const toggleExpandedOption = useContextSelector(
|
|
279
|
-
CompactRowContext,
|
|
280
|
-
(v) => v.toggleExpandedOption
|
|
281
|
-
);
|
|
282
|
-
const flashOption = useContextSelector(CompactRowContext, (v) => v.flashOption);
|
|
283
|
-
const hasOptionChanged = useContextSelector(CompactRowContext, (v) => v.hasOptionChanged);
|
|
284
|
-
const handleOptionLabelClick = useContextSelector(
|
|
285
|
-
CompactRowContext,
|
|
286
|
-
(v) => v.handleOptionLabelClick
|
|
287
|
-
);
|
|
288
|
-
const removeSelectedOption = useContextSelector(
|
|
289
|
-
CompactRowContext,
|
|
290
|
-
(v) => v.removeSelectedOption
|
|
291
|
-
);
|
|
292
|
-
const getTypeForOption = useContextSelector(CompactRowContext, (v) => v.getTypeForOption);
|
|
293
|
-
const isRendererOnly = useContextSelector(CompactRowContext, (v) => v.isRendererOnly);
|
|
294
|
-
const confirmAction = useContextSelector(CompactRowContext, (v) => v.confirmAction);
|
|
295
|
-
const optionActions = useContextSelector(CompactRowContext, (v) => v.optionActions);
|
|
296
|
-
const collapseOptionActions = useContextSelector(
|
|
297
|
-
CompactRowContext,
|
|
298
|
-
(v) => v.collapseOptionActions
|
|
299
|
-
);
|
|
300
|
-
const renderOption = useContextSelector(CompactRowContext, (v) => v.renderOption);
|
|
301
|
-
const theme = useContextSelector(CompactRowContext, (v) => v.theme);
|
|
302
|
-
const cMuted = useContextSelector(CompactRowContext, (v) => v.cMuted);
|
|
303
|
-
const templates = useContextSelector(CompactRowContext, (v) => v.templates);
|
|
304
|
-
const cKey = useContextSelector(CompactRowContext, (v) => v.cKey);
|
|
305
|
-
const cDivider = useContextSelector(CompactRowContext, (v) => v.cDivider);
|
|
306
|
-
const cHover = useContextSelector(CompactRowContext, (v) => v.cHover);
|
|
307
|
-
const cDanger = useContextSelector(CompactRowContext, (v) => v.cDanger);
|
|
308
|
-
const cWarning = useContextSelector(CompactRowContext, (v) => v.cWarning);
|
|
309
|
-
const cInfo = useContextSelector(CompactRowContext, (v) => v.cInfo);
|
|
310
|
-
const cText = useContextSelector(CompactRowContext, (v) => v.cText);
|
|
311
|
-
// A field's NAME: full text colour by default; grey in the calm look, so the
|
|
312
|
-
// value beside it is what the eye lands on. One colour for the read row, the
|
|
313
|
-
// open row and the edit card, so a name does not change as its field opens.
|
|
314
|
-
const cLabel = calm ? `${cText}b3` : cKey;
|
|
315
|
-
// A markdown value renders as a document in the row's inset; the host's
|
|
316
|
-
// renderer when it supplied one, reqraft's own otherwise.
|
|
317
|
-
|
|
318
|
-
// Value-cell content: colour adds a swatch, file an icon + size; hash keeps
|
|
319
|
-
// its "N fields" summary (sub-fields reveal beneath the row).
|
|
320
|
-
const renderReadFirstValue = (
|
|
321
|
-
field: IQorusFormField,
|
|
322
|
-
schema: TQorusFormFieldSchema | undefined,
|
|
323
|
-
formatted: string,
|
|
324
|
-
// An editable row gets one line and an ellipsis — its value line is a
|
|
325
|
-
// summary you click to open. A read-only row is the only rendering the
|
|
326
|
-
// value gets, so it wraps.
|
|
327
|
-
full = false
|
|
328
|
-
): React.ReactNode => {
|
|
329
|
-
const valueType = getValueType(field, schema);
|
|
330
|
-
const wrapStyle: React.CSSProperties = {
|
|
331
|
-
display: 'inline-flex',
|
|
332
|
-
alignItems: 'center',
|
|
333
|
-
gap: 6,
|
|
334
|
-
minWidth: 0,
|
|
335
|
-
maxWidth: '100%',
|
|
336
|
-
...(full ? { flexWrap: 'wrap' } : {}),
|
|
337
|
-
};
|
|
338
|
-
const textStyle: React.CSSProperties =
|
|
339
|
-
full ?
|
|
340
|
-
{ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }
|
|
341
|
-
: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
342
|
-
|
|
343
|
-
/* A row has to SAY when its value is an expression.
|
|
344
|
-
|
|
345
|
-
Reported from the live IDE: an author accepted the *Use as expression*
|
|
346
|
-
offer on one field and not on the other, and the two rows read exactly
|
|
347
|
-
the same - `1 + 2` and `2 + 1`, both plain text - so there was no way
|
|
348
|
-
to tell which had been converted. The assertion then compared the
|
|
349
|
-
string `"1 + 2"` against the number `3` and the author had no way to
|
|
350
|
-
see why.
|
|
351
|
-
|
|
352
|
-
The same Σ the expression editor uses, so the mark means the same thing
|
|
353
|
-
wherever it appears. */
|
|
354
|
-
const withExpressionMarker = (rendered: React.ReactNode): React.ReactNode => {
|
|
355
|
-
if (!(field as { is_expression?: boolean })?.is_expression) {
|
|
356
|
-
return rendered;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return (
|
|
360
|
-
<span style={{ ...wrapStyle, gap: 4 }}>
|
|
361
|
-
<ReqoreIcon
|
|
362
|
-
icon='Functions'
|
|
363
|
-
size='tiny'
|
|
364
|
-
/* A stable hook: reqore renders every icon as a bare
|
|
365
|
-
`.reqore-icon`, so without this there is nothing to select the
|
|
366
|
-
mark by — in a test or in a consumer's stylesheet. */
|
|
367
|
-
className='reqraft-expression-marker'
|
|
368
|
-
/* Opacity through `effect`, which is how this repo dims a reqore
|
|
369
|
-
component (see the card's `short_desc` below); only the flex
|
|
370
|
-
rule, which reqore has no prop for, stays inline. */
|
|
371
|
-
effect={{ opacity: 0.7 }}
|
|
372
|
-
style={{ flexShrink: 0 }}
|
|
373
|
-
tooltip='This value is an expression'
|
|
374
|
-
/>
|
|
375
|
-
{rendered}
|
|
376
|
-
</span>
|
|
377
|
-
);
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
if (valueType === 'rgbcolor') {
|
|
381
|
-
const swatch = colorToCss(field?.value);
|
|
382
|
-
return (
|
|
383
|
-
<span
|
|
384
|
-
style={
|
|
385
|
-
swatch ?
|
|
386
|
-
{
|
|
387
|
-
...wrapStyle,
|
|
388
|
-
// a slight wash of the chosen colour behind the swatch + hex
|
|
389
|
-
background: `color-mix(in srgb, ${swatch} 14%, transparent)`,
|
|
390
|
-
padding: '1px 8px',
|
|
391
|
-
borderRadius: 4,
|
|
392
|
-
}
|
|
393
|
-
: wrapStyle
|
|
394
|
-
}
|
|
395
|
-
>
|
|
396
|
-
{swatch ?
|
|
397
|
-
<StyledColorSwatch aria-hidden $color={swatch} $border={cDivider} />
|
|
398
|
-
: null}
|
|
399
|
-
<span style={textStyle}>{formatted}</span>
|
|
400
|
-
</span>
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (valueType === 'file') {
|
|
405
|
-
const fileSize = getFileSize(field?.value);
|
|
406
|
-
// Match the File editor's chip: info-blue, FileLine icon, size as the
|
|
407
|
-
// trailing key pill (the editor renders it as a ReqoreButton badge).
|
|
408
|
-
return (
|
|
409
|
-
<ReqoreTag
|
|
410
|
-
size='small'
|
|
411
|
-
minimal
|
|
412
|
-
intent='info'
|
|
413
|
-
icon='FileLine'
|
|
414
|
-
label={formatted}
|
|
415
|
-
labelKey={fileSize !== undefined ? formatBytes(fileSize) : undefined}
|
|
416
|
-
/>
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (valueType === 'bool' || valueType === 'boolean') {
|
|
421
|
-
// Calm: Yes and No are words. Neither is a status — "No" is not a
|
|
422
|
-
// failure — so neither is coloured.
|
|
423
|
-
if (calm) {
|
|
424
|
-
return <span style={textStyle}>{formatted}</span>;
|
|
425
|
-
}
|
|
426
|
-
const truthy = field?.value === true || field?.value === 'true';
|
|
427
|
-
return (
|
|
428
|
-
<ReqoreTag
|
|
429
|
-
size='small'
|
|
430
|
-
minimal
|
|
431
|
-
intent={truthy ? 'success' : 'danger'}
|
|
432
|
-
label={formatted}
|
|
433
|
-
/>
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// A test's cases read as CHIPS here, the same as the names in the table
|
|
438
|
-
// this row opens (`CasesTable`, qorus-ide). `test-cases` is a
|
|
439
|
-
// renderer-only ui_type, so it reached none of the branches above and
|
|
440
|
-
// fell through to the generic summary, which flattens the array of case
|
|
441
|
-
// hashes to a comma-joined string — "case_1, resolved_expected_values".
|
|
442
|
-
// A case name is an identifier the author types into `$.` references and
|
|
443
|
-
// skip conditions, and a chip is how the rest of the product writes an
|
|
444
|
-
// identifier down; the table already agreed, so the collapsed row was the
|
|
445
|
-
// only surface still printing them as prose.
|
|
446
|
-
//
|
|
447
|
-
// `intent` is a FILL, which is what a tag is — the hazard the other
|
|
448
|
-
// branches warn about is only ever using an intent as a TEXT colour.
|
|
449
|
-
//
|
|
450
|
-
// Falls through to the plain summary unless EVERY entry yields a name: a
|
|
451
|
-
// partly-chipped row would read as though the unnamed cases were missing.
|
|
452
|
-
if (valueType === 'test-cases' && Array.isArray(field?.value)) {
|
|
453
|
-
const caseNames = (field.value as unknown[]).map((item) => {
|
|
454
|
-
const name = (item as { name?: unknown } | null | undefined)?.name;
|
|
455
|
-
return typeof name === 'string' ? name.trim() : '';
|
|
456
|
-
});
|
|
457
|
-
if (caseNames.length > 0 && caseNames.every((name) => name !== '')) {
|
|
458
|
-
return (
|
|
459
|
-
<ReqoreControlGroup
|
|
460
|
-
gapSize='tiny'
|
|
461
|
-
verticalAlign='center'
|
|
462
|
-
// An editable row is ONE line: further chips clip with the row
|
|
463
|
-
// instead of growing it. A read-only row is the only rendering
|
|
464
|
-
// the value gets, so it wraps and shows every case.
|
|
465
|
-
wrap={full}
|
|
466
|
-
style={full ? undefined : { minWidth: 0, overflow: 'hidden' }}
|
|
467
|
-
>
|
|
468
|
-
{caseNames.map((name, index) => (
|
|
469
|
-
<ReqoreTag
|
|
470
|
-
key={`${name}-${index}`}
|
|
471
|
-
className='options-readfirst-case-tag'
|
|
472
|
-
size='small'
|
|
473
|
-
minimal
|
|
474
|
-
intent='info'
|
|
475
|
-
label={name}
|
|
476
|
-
/>
|
|
477
|
-
))}
|
|
478
|
-
</ReqoreControlGroup>
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Code-editor read summary: replace the truncated string with a chip
|
|
484
|
-
// showing line count + character count. The actual code renders below
|
|
485
|
-
// the row as a monospace `<pre>` inside a `ReqoreCollapsibleContent` —
|
|
486
|
-
// see `showCodePreview` below. Keeps the row height fixed while making
|
|
487
|
-
// multi-line source readable without opening the full editor.
|
|
488
|
-
if (valueType === 'code-editor' && typeof field?.value === 'string') {
|
|
489
|
-
// An absorbed sibling has no row of its own, so a COLLAPSED host has to
|
|
490
|
-
// carry its value or it disappears from the form entirely when closed.
|
|
491
|
-
// It reads as a chip beside the size chip — "Qore · 62 lines" — which
|
|
492
|
-
// is the same summary the read-only interface pages already show.
|
|
493
|
-
return (
|
|
494
|
-
<ReqoreControlGroup gapSize='tiny' verticalAlign='center' wrap>
|
|
495
|
-
{(absorbedFields ?? []).map((absorbedName) => {
|
|
496
|
-
const absorbedValue = (
|
|
497
|
-
availableOptions?.[absorbedName] as IQorusFormField | undefined
|
|
498
|
-
)?.value;
|
|
499
|
-
if (absorbedValue === undefined || absorbedValue === null || absorbedValue === '') {
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
return (
|
|
503
|
-
<ReqoreTag
|
|
504
|
-
key={absorbedName}
|
|
505
|
-
className='options-readfirst-absorbed-tag'
|
|
506
|
-
size='small'
|
|
507
|
-
minimal
|
|
508
|
-
label={String(absorbedValue)}
|
|
509
|
-
tooltip={getAbsorbedLabel(absorbedName)}
|
|
510
|
-
/>
|
|
511
|
-
);
|
|
512
|
-
})}
|
|
513
|
-
</ReqoreControlGroup>
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Markdown read summary: the row prints the value's prose (the markdown
|
|
518
|
-
// markers are stripped by `formatOptionValue`), so a multi-line document
|
|
519
|
-
// gets a chip carrying what the one truncated line cannot -- how much
|
|
520
|
-
// more there is -- and renders in full in the inset below, exactly as a
|
|
521
|
-
// code value does. A single-line value needs neither: its one line IS
|
|
522
|
-
// the whole document, already rendered as prose.
|
|
523
|
-
if (
|
|
524
|
-
valueType === 'markdown' &&
|
|
525
|
-
typeof field?.value === 'string' &&
|
|
526
|
-
isMultilineMarkdown(field.value)
|
|
527
|
-
) {
|
|
528
|
-
// Prose only. The line count is metadata ABOUT the field, so it sits
|
|
529
|
-
// under the field's name (see `lineCountNote`) rather than in front of
|
|
530
|
-
// its content — the value column carries the value and nothing else.
|
|
531
|
-
//
|
|
532
|
-
// This branch is reached only when no host renderer is present; with one,
|
|
533
|
-
// the whole value line is suppressed in favour of the rendered document
|
|
534
|
-
// below (see `showMarkdownPreview` at the value span).
|
|
535
|
-
return <span style={textStyle}>{formatted}</span>;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Selected allowed-value / enum item with a logo (e.g. language images):
|
|
539
|
-
// render it beside the value, where it belongs — not as the field's icon.
|
|
540
|
-
const allowedImage = getAllowedValueImage(field?.value, schema);
|
|
541
|
-
if (allowedImage) {
|
|
542
|
-
return (
|
|
543
|
-
<span style={wrapStyle}>
|
|
544
|
-
<ReqoreIcon image={allowedImage} size='16px' style={{ flexShrink: 0 }} />
|
|
545
|
-
<span style={textStyle}>{formatted}</span>
|
|
546
|
-
</span>
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// The same, for an option whose mark is an ICON rather than a logo. Only
|
|
551
|
-
// images were drawn here, so a list whose entries are told apart by their
|
|
552
|
-
// icon — every interface kind is a bare word, `Service`, `Job`, `Qog` —
|
|
553
|
-
// showed the mark while choosing and lost it the moment the row went
|
|
554
|
-
// read-only, which is where a reader spends most of their time.
|
|
555
|
-
const allowedIcon = getAllowedValueIcon(field?.value, schema);
|
|
556
|
-
if (allowedIcon) {
|
|
557
|
-
return (
|
|
558
|
-
<span style={wrapStyle}>
|
|
559
|
-
<ReqoreIcon icon={allowedIcon as any} size='16px' style={{ flexShrink: 0 }} />
|
|
560
|
-
<span style={textStyle}>{formatted}</span>
|
|
561
|
-
</span>
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Template value ($local:…): the read-only template picker — the SAME chip
|
|
566
|
-
// TemplateField renders when disabled, so a template reads identically here
|
|
567
|
-
// and in the editor (qorus-ide intent scheme: info / qorus purple).
|
|
568
|
-
//
|
|
569
|
-
// A field may also declare templates OF ITS OWN, and a value drawn from
|
|
570
|
-
// that list is just as much a template even when it does not look like
|
|
571
|
-
// `$name:key` — `isValueTemplate` is a guess at the shape of the built-in
|
|
572
|
-
// grammar, and a host's own grammar (a Qorus test's `$.order.id`) fails it.
|
|
573
|
-
// Asking the field's list whether it CONTAINS the value settles it without
|
|
574
|
-
// guessing at syntax, and hands back the entry whose label the chip prints,
|
|
575
|
-
// so a reference reads as the name it was chosen by here as well as in the
|
|
576
|
-
// editor rather than as a raw path.
|
|
577
|
-
const ownTemplates = (schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates;
|
|
578
|
-
// The grammar the field declares its references in, when it speaks one of
|
|
579
|
-
// its own — the same schema key its rich-text editor is given.
|
|
580
|
-
const templateGrammar = (schema as TFieldWithOwnTemplates | undefined)?.templateToken;
|
|
581
|
-
if (
|
|
582
|
-
typeof field?.value === 'string' &&
|
|
583
|
-
!(field as { is_expression?: boolean }).is_expression &&
|
|
584
|
-
(isValueTemplate(field.value) || !!findTemplate(ownTemplates ?? {}, field.value))
|
|
585
|
-
) {
|
|
586
|
-
return <ReadOnlyTemplateTag value={field.value} templates={ownTemplates} size='small' />;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// A LIST whose elements are rich-text envelopes is a list of strings, so it
|
|
590
|
-
// reads as those strings. Without this the row fell through to the generic
|
|
591
|
-
// list summary and printed a bare count ("1 item") above the structured
|
|
592
|
-
// tree, never the address itself.
|
|
593
|
-
if (
|
|
594
|
-
Array.isArray(field?.value) &&
|
|
595
|
-
field.value.some((item) => richtextItemText(item) !== undefined)
|
|
596
|
-
) {
|
|
597
|
-
const texts = field.value.map((item) => richtextItemText(item) ?? String(item ?? ''));
|
|
598
|
-
return <span style={textStyle}>{texts.filter((text) => text !== '').join(', ')}</span>;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Richtext read-first summary: inline $-chips for embedded template tags
|
|
602
|
-
// with the prose around them (the IDE intent scheme via getTemplateTagStyle),
|
|
603
|
-
// kept lightweight instead of mounting the full editor — that lives in the
|
|
604
|
-
// expanded card. Pure-text richtext (no tags) falls through to the plain
|
|
605
|
-
// `formatted` string and keeps the single-line ellipsis.
|
|
606
|
-
if (valueType === 'richtext' && Array.isArray(field?.value)) {
|
|
607
|
-
const segments = richtextToSegments(field.value as never);
|
|
608
|
-
if (segments.some((segment) => segment.kind === 'tag')) {
|
|
609
|
-
return <TemplateText segments={segments} templates={ownTemplates} full={full} />;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// An expression reads as DPQL wherever it is shown without being edited:
|
|
614
|
-
// the same rendering its Explain panel and Preview use — monospace,
|
|
615
|
-
// coloured by the language server, template references as chips — so a
|
|
616
|
-
// collapsed row and the editor it opens never disagree about what the
|
|
617
|
-
// value looks like. See `DpqlRendering`.
|
|
618
|
-
if ((field as { is_expression?: boolean })?.is_expression && formatted) {
|
|
619
|
-
return withExpressionMarker(
|
|
620
|
-
<span style={{ minWidth: 0, flex: '1 1 auto', ...(full ? {} : { overflow: 'hidden' }) }}>
|
|
621
|
-
<DpqlRendering text={formatted} templates={ownTemplates} />
|
|
622
|
-
</span>
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/* A value that MENTIONS a reference rather than being one.
|
|
627
|
-
|
|
628
|
-
The branch above answers "is this value a template?", and a predicate is
|
|
629
|
-
not: `$._case.mode != 'simulate'` is a comparison the author wrote with a
|
|
630
|
-
reference inside it. So it fell all the way through to the plain string
|
|
631
|
-
and the row printed the engine's own spelling of a value the author had
|
|
632
|
-
picked by name — the one thing the template mechanism exists to stop.
|
|
633
|
-
Reported against the Skip-when field of a test case, where the list
|
|
634
|
-
above the drawer already named it and the drawer contradicted it.
|
|
635
|
-
|
|
636
|
-
Drawn from `formatted` rather than from the raw value, so everything the
|
|
637
|
-
formatter already did — a joined list, a sensitive value's dots — still
|
|
638
|
-
holds, and only the references inside the resulting line become chips.
|
|
639
|
-
The author's own text around them survives exactly as typed. */
|
|
640
|
-
const templateSegments =
|
|
641
|
-
typeof formatted === 'string' && formatted ?
|
|
642
|
-
templateTextSegments(formatted, ownTemplates, templateGrammar)
|
|
643
|
-
: [];
|
|
644
|
-
if (templateSegments.some((segment) => segment.kind === 'tag')) {
|
|
645
|
-
return withExpressionMarker(
|
|
646
|
-
<TemplateText segments={templateSegments} templates={ownTemplates} full={full} />
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return withExpressionMarker(full ? <span style={textStyle}>{formatted}</span> : formatted);
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
const schema = options?.[optionName];
|
|
654
|
-
const label = schema?.display_name || optionName;
|
|
655
|
-
const injectedOptionActions = React.useMemo<IReqorePanelAction[]>(
|
|
656
|
-
() =>
|
|
657
|
-
schema ?
|
|
658
|
-
resolveOptionActions(optionActions, {
|
|
659
|
-
name: optionName,
|
|
660
|
-
schema,
|
|
661
|
-
value: availableOptions?.[optionName],
|
|
662
|
-
}).filter((action) => action.show !== false)
|
|
663
|
-
: [],
|
|
664
|
-
[availableOptions?.[optionName], optionActions, optionName, schema]
|
|
665
|
-
);
|
|
666
|
-
// Which injected actions stay as inline buttons, and which move into the
|
|
667
|
-
// overflow menu — see `splitOptionActions`.
|
|
668
|
-
const [inlineOptionActions, menuOptionActions] = React.useMemo<
|
|
669
|
-
[IReqorePanelAction[], IReqorePanelAction[]]
|
|
670
|
-
>(
|
|
671
|
-
() => splitOptionActions(injectedOptionActions, !!collapseOptionActions),
|
|
672
|
-
[collapseOptionActions, injectedOptionActions]
|
|
673
|
-
);
|
|
674
|
-
const injectedOptionActionMenuItems = React.useMemo<IReqoreDropdownItem[]>(
|
|
675
|
-
() => menuOptionActions.map(optionActionAsMenuItem),
|
|
676
|
-
[menuOptionActions]
|
|
677
|
-
);
|
|
678
|
-
const renderInjectedOptionAction = (
|
|
679
|
-
action: IReqorePanelAction,
|
|
680
|
-
index: number,
|
|
681
|
-
size: 'tiny' | 'small' = 'small'
|
|
682
|
-
) => {
|
|
683
|
-
// `show` drives visibility here instead of reaching the DOM. The classic
|
|
684
|
-
// path hands these to ReqorePanel, which honours `show: 'hover'` itself;
|
|
685
|
-
// the compact slots render plain buttons, so 'hover' becomes a CSS gate on
|
|
686
|
-
// the containing row/card (see compactRowStyles) — same config, same
|
|
687
|
-
// behaviour in both modes.
|
|
688
|
-
// `size` is destructured out and deliberately NOT honoured: the row owns
|
|
689
|
-
// its action strip's rhythm, and a consumer-supplied size made the
|
|
690
|
-
// injected button visibly smaller than the revert / more / confirm
|
|
691
|
-
// buttons beside it. The contextual `size` argument is the row's own
|
|
692
|
-
// (small when editing or in a card, tiny on a read row), so injected
|
|
693
|
-
// actions always match their neighbours.
|
|
694
|
-
const {
|
|
695
|
-
label: actionLabel,
|
|
696
|
-
onClick,
|
|
697
|
-
show,
|
|
698
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
699
|
-
size: _ignoredSize,
|
|
700
|
-
className,
|
|
701
|
-
...actionProps
|
|
702
|
-
} = action;
|
|
703
|
-
|
|
704
|
-
return (
|
|
705
|
-
<ReqoreButton
|
|
706
|
-
key={`${optionName}-injected-action-${index}`}
|
|
707
|
-
size={size}
|
|
708
|
-
minimal
|
|
709
|
-
flat
|
|
710
|
-
fixed
|
|
711
|
-
{...actionProps}
|
|
712
|
-
className={`${className ? `${className} ` : ''}options-injected-action${
|
|
713
|
-
show === 'hover' ? ' options-injected-action-hover' : ''
|
|
714
|
-
}`}
|
|
715
|
-
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
|
716
|
-
event.stopPropagation();
|
|
717
|
-
onClick?.();
|
|
718
|
-
}}
|
|
719
|
-
>
|
|
720
|
-
{actionLabel}
|
|
721
|
-
</ReqoreButton>
|
|
722
|
-
);
|
|
723
|
-
};
|
|
724
|
-
const required = !!(schema?.required || schema?.required_groups);
|
|
725
|
-
// The asterisk says "you have to fill this in". Read-only, nobody does. The
|
|
726
|
-
// calm look draws none at all: an empty required field says so in its
|
|
727
|
-
// value column instead ("Required · add a value"), and a filled one has
|
|
728
|
-
// nothing left to ask for.
|
|
729
|
-
const requiredMark = required && !readOnly && !calm;
|
|
730
|
-
const removable =
|
|
731
|
-
!readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
|
|
732
|
-
const changed = !hidden && !readOnly && hasOptionChanged(optionField?.value, optionName);
|
|
733
|
-
// "Has a value" = set to anything non-empty. Drives the edit-row Clear
|
|
734
|
-
// button and the cluster node's filled state (see `memberSet`).
|
|
735
|
-
/* "Is there anything to clear?" — this predicate has exactly one job, and
|
|
736
|
-
both of its uses are the Clear-value button.
|
|
737
|
-
|
|
738
|
-
An empty list is not a value. `[]` is neither `undefined`, `null` nor `''`,
|
|
739
|
-
so a field whose last item had just been removed went on offering to clear
|
|
740
|
-
it, and the confirmation asked whether to clear nothing. A test's cases
|
|
741
|
-
table is where this shows: delete the last case and a red clear-all sat
|
|
742
|
-
above an empty table, guarding a no-op.
|
|
743
|
-
|
|
744
|
-
Deliberately arrays only. An empty hash is arguable — "present but empty"
|
|
745
|
-
can be a real state for a hash — and nothing has demonstrated the same
|
|
746
|
-
defect there, so widening this predicate would be a guess. */
|
|
747
|
-
const hasValue =
|
|
748
|
-
optionField?.value !== undefined &&
|
|
749
|
-
optionField?.value !== null &&
|
|
750
|
-
optionField?.value !== '' &&
|
|
751
|
-
!(Array.isArray(optionField.value) && optionField.value.length === 0);
|
|
752
|
-
// Required-group membership shows a PERSISTENT chip on every member: amber
|
|
753
|
-
// "One of" while the group is unmet (tap → flash siblings), then a muted-green
|
|
754
|
-
// resolution once satisfied — "Covers" on the field that satisfied it,
|
|
755
|
-
// "Covered by <X>" on the rest. The resolution folds in what used to be a
|
|
756
|
-
// separate value-slot note.
|
|
757
|
-
const memberGroups: string[] = (schema?.required_groups as string[]) || [];
|
|
758
|
-
const unmetGroups = memberGroups.filter(
|
|
759
|
-
(groupName) => !requiredGroupsInfo.satisfiedBy[groupName]
|
|
760
|
-
);
|
|
761
|
-
const coveredByGroup = memberGroups.find(
|
|
762
|
-
(groupName) =>
|
|
763
|
-
requiredGroupsInfo.satisfiedBy[groupName] &&
|
|
764
|
-
requiredGroupsInfo.satisfiedBy[groupName] !== optionName
|
|
765
|
-
);
|
|
766
|
-
const coveredByLabel =
|
|
767
|
-
coveredByGroup && !schema?.required ?
|
|
768
|
-
(options?.[requiredGroupsInfo.satisfiedBy[coveredByGroup] as string]
|
|
769
|
-
?.display_name as string) || requiredGroupsInfo.satisfiedBy[coveredByGroup]
|
|
770
|
-
: undefined;
|
|
771
|
-
// Unmet → drive the chip off the unmet groups; resolved → keep listing the
|
|
772
|
-
// members so the "covers / covered by" chip stays a locate-the-siblings
|
|
773
|
-
// dropdown rather than going inert.
|
|
774
|
-
const chipGroups = unmetGroups.length ? unmetGroups : memberGroups;
|
|
775
|
-
const groupResolved = !!memberGroups.length && !unmetGroups.length;
|
|
776
|
-
const requiredGroupChip =
|
|
777
|
-
!hidden && !readOnly && memberGroups.length && !schema?.required ?
|
|
778
|
-
<span
|
|
779
|
-
style={{ display: 'inline-flex' }}
|
|
780
|
-
role='presentation'
|
|
781
|
-
onClick={(e) => e.stopPropagation()}
|
|
782
|
-
onKeyDown={(e) => e.stopPropagation()}
|
|
783
|
-
onMouseEnter={() =>
|
|
784
|
-
setHighlightedOptions(requiredGroupsInfo.members[chipGroups[0]] || [])
|
|
785
|
-
}
|
|
786
|
-
onMouseLeave={() => setHighlightedOptions([])}
|
|
787
|
-
>
|
|
788
|
-
<ReqoreDropdown
|
|
789
|
-
className='options-readfirst-required-group'
|
|
790
|
-
size='tiny'
|
|
791
|
-
minimal
|
|
792
|
-
flat
|
|
793
|
-
compact
|
|
794
|
-
effect={calm ? undefined : { uppercase: true, spaced: 1 }}
|
|
795
|
-
// Calm: a met group is nothing to point at — only the unmet one is
|
|
796
|
-
// coloured.
|
|
797
|
-
intent={
|
|
798
|
-
groupResolved ?
|
|
799
|
-
calm ? undefined
|
|
800
|
-
: 'success'
|
|
801
|
-
: 'warning'
|
|
802
|
-
}
|
|
803
|
-
icon={groupResolved ? 'CheckLine' : 'LinkM'}
|
|
804
|
-
label={
|
|
805
|
-
!groupResolved ? 'One of'
|
|
806
|
-
: coveredByLabel ?
|
|
807
|
-
`Covered by “${coveredByLabel}”`
|
|
808
|
-
: 'Covers'
|
|
809
|
-
}
|
|
810
|
-
filterable={
|
|
811
|
-
chipGroups.reduce((n, g) => n + requiredGroupsInfo.members[g].length, 0) > 6
|
|
812
|
-
}
|
|
813
|
-
items={chipGroups.flatMap((groupName): IReqoreDropdownItem[] => [
|
|
814
|
-
...(chipGroups.length > 1 ?
|
|
815
|
-
[{ divider: true, label: groupName, dividerAlign: 'left' } as IReqoreDropdownItem]
|
|
816
|
-
: []),
|
|
817
|
-
...requiredGroupsInfo.members[groupName].map((member): IReqoreDropdownItem => ({
|
|
818
|
-
label: (options?.[member]?.display_name as string) || member,
|
|
819
|
-
icon: member === optionName ? 'MapPinLine' : 'ArrowRightLine',
|
|
820
|
-
selected: member === optionName,
|
|
821
|
-
disabled: member === optionName,
|
|
822
|
-
onClick: member === optionName ? undefined : () => flashOption(member),
|
|
823
|
-
})),
|
|
824
|
-
])}
|
|
825
|
-
/>
|
|
826
|
-
</span>
|
|
827
|
-
: null;
|
|
828
|
-
const fixedAllowedValueOption = isFixedCompactAllowedValueOption(schema);
|
|
829
|
-
const editType =
|
|
830
|
-
(fixedAllowedValueOption ?
|
|
831
|
-
(schema?.type as string) || (schema?.ui_type as string)
|
|
832
|
-
: (schema?.ui_type as string) || (schema?.type as string)) ?? '';
|
|
833
|
-
// Scalars edit in place inside the row; complex fields (tall or nested
|
|
834
|
-
// editors) still open the expanded card below.
|
|
835
|
-
const inlineEditableNow =
|
|
836
|
-
!readOnly &&
|
|
837
|
-
!schema?.arg_schema &&
|
|
838
|
-
!(operators && size(operators)) &&
|
|
839
|
-
// Expression fields open the builder/DPQL editor — too tall for inline.
|
|
840
|
-
!(optionField as { is_expression?: boolean }).is_expression &&
|
|
841
|
-
// A choice with per-option logos (e.g. language) reads better collapsed.
|
|
842
|
-
!optionHasImages(schema) &&
|
|
843
|
-
!COMPACT_COMPLEX_TYPES.has(editType);
|
|
844
|
-
|
|
845
|
-
/* Which of the two editors a row gets is settled when the row OPENS.
|
|
846
|
-
*
|
|
847
|
-
* Every term above reads the VALUE or a schema derived from it, so the
|
|
848
|
-
* answer can change while the author is inside the control — and the two
|
|
849
|
-
* branches are different subtrees, so a changed answer does not restyle
|
|
850
|
-
* the editor, it unmounts one and mounts the other. Everything the old one
|
|
851
|
-
* held goes with it: what was typed, the caret, and any state the editor
|
|
852
|
-
* keeps about how it was entered.
|
|
853
|
-
*
|
|
854
|
-
* `is_expression` is the term that actually fires, and it fires on the one
|
|
855
|
-
* interaction that exists to keep the author where they are: accepting
|
|
856
|
-
* "Use as expression" is the moment the value becomes an expression, and
|
|
857
|
-
* the expression shell opens on its Text view precisely because the author
|
|
858
|
-
* has just written the expression as text. The remount discarded that and
|
|
859
|
-
* dropped them into the visual builder, so the sentence they had typed was
|
|
860
|
-
* nowhere on screen. Clicking Text then appeared to work and bounced back,
|
|
861
|
-
* because the next value change remounted the row again.
|
|
862
|
-
*
|
|
863
|
-
* So the row keeps the editor it opened with for as long as it is open,
|
|
864
|
-
* and asks the question again when it is next opened — the same trade
|
|
865
|
-
* `editEntryValue` above makes for a different reason, and the one
|
|
866
|
-
* qorus-ide's `referenceEditorLatch` makes for this exact one. A field that
|
|
867
|
-
* already holds an expression when it is opened still gets the card; it is
|
|
868
|
-
* only a field that BECOMES one under the author that stays put.
|
|
869
|
-
*
|
|
870
|
-
* Held in a ref and settled during render rather than in an effect: an
|
|
871
|
-
* effect lands one render late, and one render late is a visible flash of
|
|
872
|
-
* the wrong editor — which is the thing being fixed.
|
|
873
|
-
*/
|
|
874
|
-
const inlineEditableLatch = React.useRef<boolean | undefined>(undefined);
|
|
875
|
-
/* Whether THIS opening was the author asking to change the value.
|
|
876
|
-
*
|
|
877
|
-
* A click (or Enter/Space) on a closed row means "let me edit this"; a row
|
|
878
|
-
* opened by the form itself — `initialExpandedOptions` from an address, the
|
|
879
|
-
* first-attention row, a diagram opening the step it is showing — means
|
|
880
|
-
* "here it is". Only the first answers the question the picker answers, so
|
|
881
|
-
* only the first opens one (see the caret effect below). A ref, because it
|
|
882
|
-
* is read by the effect that runs after the open and must not itself cause
|
|
883
|
-
* a render. */
|
|
884
|
-
const openedByAuthor = React.useRef(false);
|
|
885
|
-
if (!isExpanded) {
|
|
886
|
-
inlineEditableLatch.current = undefined;
|
|
887
|
-
} else if (inlineEditableLatch.current === undefined) {
|
|
888
|
-
inlineEditableLatch.current = inlineEditableNow;
|
|
889
|
-
}
|
|
890
|
-
const inlineEditable = isExpanded ? !!inlineEditableLatch.current : inlineEditableNow;
|
|
891
|
-
|
|
892
|
-
/* The author's intent survives until the row is closed again, and no longer.
|
|
893
|
-
*
|
|
894
|
-
* An EFFECT, keyed on the row actually closing, rather than a line in the
|
|
895
|
-
* render body: between the click and the row opening there is a committed
|
|
896
|
-
* render that still reads `expandedOptions` without this row in it (the
|
|
897
|
-
* context propagates a beat after the handler runs), and clearing the flag
|
|
898
|
-
* on every collapsed render cleared it in that gap — the picker then never
|
|
899
|
-
* opened, which is the defect this whole mechanism exists to fix. Keyed on
|
|
900
|
-
* `isExpanded`, the transient render is not a change and the effect does
|
|
901
|
-
* not run; a genuine collapse is, and it does. */
|
|
902
|
-
React.useEffect(() => {
|
|
903
|
-
if (!isExpanded) {
|
|
904
|
-
openedByAuthor.current = false;
|
|
905
|
-
}
|
|
906
|
-
}, [isExpanded]);
|
|
907
|
-
|
|
908
|
-
/* The instruction the row gives its editor when it opens into a picker.
|
|
909
|
-
*
|
|
910
|
-
* State, not a call on the control: reqore's popover binds its own click
|
|
911
|
-
* listener in an effect keyed on the trigger element arriving, so a
|
|
912
|
-
* synthetic click dispatched from the row's own effect lands before
|
|
913
|
-
* anything is listening and does nothing at all. The editor opening itself
|
|
914
|
-
* from a prop has no such ordering to lose. */
|
|
915
|
-
const [openThePicker, setOpenThePicker] = React.useState(false);
|
|
916
|
-
React.useEffect(() => {
|
|
917
|
-
// One shot: true for the commit the editor acts on, false again after.
|
|
918
|
-
if (openThePicker) {
|
|
919
|
-
setOpenThePicker(false);
|
|
920
|
-
}
|
|
921
|
-
}, [openThePicker]);
|
|
922
|
-
|
|
923
|
-
// Put the caret in the row's editor when the row opens, so the reader can
|
|
924
|
-
// answer straight away (the prototype's tap-to-edit feel) — and so
|
|
925
|
-
// `autoFocusFirstRequired` means what it says.
|
|
926
|
-
//
|
|
927
|
-
// Whatever control the row OFFERS: `findRowFocusTarget` knows a pick-one is
|
|
928
|
-
// a set of `[tabindex]` checkboxes and a selector is a `<button>`, which a
|
|
929
|
-
// text-only scan skipped — so the engine opened the right row and the caret
|
|
930
|
-
// never moved. That is most of the first questions a form asks.
|
|
931
|
-
//
|
|
932
|
-
// The row is revealed before the caret lands in it, `block: 'nearest'` on
|
|
933
|
-
// the ROW. A caret nobody can see is not a caret: a form opened by a click
|
|
934
|
-
// usually mounts below the fold, and focusing something 700px down changes
|
|
935
|
-
// nothing on screen while the next keystroke goes somewhere invisible.
|
|
936
|
-
// `nearest` is by spec a no-op for a row already in view, so a form on
|
|
937
|
-
// screen never jumps, and it is the row that carries the label saying what
|
|
938
|
-
// the question is. A caller that wants the row opened WITHOUT the caret
|
|
939
|
-
// has `expandFirstRequired` for exactly that.
|
|
940
|
-
//
|
|
941
|
-
// A MutationObserver, not a timer: a host editor that resolves a catalogue
|
|
942
|
-
// before it can draw a control mounts it several commits after this effect
|
|
943
|
-
// runs, and there is no fixed time to wait for it — the control appearing
|
|
944
|
-
// IS the event. The 60ms timer this replaces missed every such editor, and
|
|
945
|
-
// fired too early or too late for every other one. One shot, and it gives
|
|
946
|
-
// up the moment the caret belongs to someone outside this row, so it can
|
|
947
|
-
// never pull a reader out of a control they moved into themselves.
|
|
948
|
-
const editorRef = React.useRef<HTMLDivElement>(null);
|
|
949
|
-
const rowRef = React.useRef<HTMLDivElement>(null);
|
|
950
|
-
React.useEffect(() => {
|
|
951
|
-
if (!isExpanded) return undefined;
|
|
952
|
-
const editor = editorRef.current;
|
|
953
|
-
if (!editor) return undefined;
|
|
954
|
-
let settled = false;
|
|
955
|
-
/* A control that is not a text box holds the caret PROVISIONALLY. An editor that
|
|
956
|
-
takes text can mount after the row opens (a code editor loads late), and until
|
|
957
|
-
it does the only control there is its chrome: a fullscreen toggle. When the
|
|
958
|
-
text box arrives it takes the caret, unless the reader has moved it since. A
|
|
959
|
-
row that never grows one (a pick-one) simply keeps its control. */
|
|
960
|
-
let provisional: HTMLElement | null = null;
|
|
961
|
-
const takeCaret = (): boolean => {
|
|
962
|
-
if (settled) return true;
|
|
963
|
-
const active = document.activeElement as HTMLElement | null;
|
|
964
|
-
if (provisional) {
|
|
965
|
-
if (active !== provisional) {
|
|
966
|
-
// The reader moved the caret themselves. Leave it there.
|
|
967
|
-
settled = true;
|
|
968
|
-
return true;
|
|
969
|
-
}
|
|
970
|
-
const textEntry = findRowFocusTarget(editor, { textEntryOnly: true });
|
|
971
|
-
if (!textEntry) return false;
|
|
972
|
-
textEntry.focus({ preventScroll: true });
|
|
973
|
-
// An editor's text box can be in the DOM before it can take focus (Monaco's
|
|
974
|
-
// edit area, mid-layout), and `focus()` then does nothing. Only a caret
|
|
975
|
-
// that landed counts; otherwise try again on the editor's next change.
|
|
976
|
-
if (document.activeElement !== textEntry) return false;
|
|
977
|
-
settled = true;
|
|
978
|
-
return true;
|
|
979
|
-
}
|
|
980
|
-
if (active && active !== document.body && !rowRef.current?.contains(active)) {
|
|
981
|
-
// Someone else holds the caret — a field the reader moved into while
|
|
982
|
-
// this row's editor was still arriving. Leave it where it is.
|
|
983
|
-
settled = true;
|
|
984
|
-
return true;
|
|
985
|
-
}
|
|
986
|
-
// A card hands the caret only to where text goes (see `findRowFocusTarget`).
|
|
987
|
-
const control = findRowFocusTarget(editor, { textEntryOnly: !inlineEditable });
|
|
988
|
-
if (!control) return false;
|
|
989
|
-
// The least scroll that shows the row clear of the pinned box header,
|
|
990
|
-
// and none when it is already in view (see `revealRow`).
|
|
991
|
-
if (rowRef.current) {
|
|
992
|
-
revealRow(rowRef.current, 'auto');
|
|
993
|
-
}
|
|
994
|
-
// `preventScroll`, because the reveal above has already put the row
|
|
995
|
-
// where it belongs; letting focus scroll as well moves it twice.
|
|
996
|
-
control.focus({ preventScroll: true });
|
|
997
|
-
// A control that cannot take focus yet (still laying out) did not take
|
|
998
|
-
// the caret: try again on the editor's next change.
|
|
999
|
-
if (document.activeElement !== control) return false;
|
|
1000
|
-
/* …and if the control the row offers is a PICKER, open it.
|
|
1001
|
-
*
|
|
1002
|
-
* A closed picker prints the value it holds, which is exactly what the
|
|
1003
|
-
* read row printed — so the row opened, nothing on screen changed, and
|
|
1004
|
-
* changing the value still cost a second click. Read-first is the
|
|
1005
|
-
* COLLAPSED row's job; an open row is the editor.
|
|
1006
|
-
*
|
|
1007
|
-
* Narrow on purpose, and each term is a case where read-first is still
|
|
1008
|
-
* right: only a row the author opened themselves (a form that opens a
|
|
1009
|
-
* row to SHOW it must not throw a list over it), only an inline scalar
|
|
1010
|
-
* row (a complex field opens a card that is a form in its own right —
|
|
1011
|
-
* its first control is one of several and nobody asked for it), and
|
|
1012
|
-
* only a control that declares itself a picker, so nothing else is
|
|
1013
|
-
* ever clicked on the author's behalf. */
|
|
1014
|
-
if (openedByAuthor.current && inlineEditable && isClosedPickerTrigger(control)) {
|
|
1015
|
-
openedByAuthor.current = false;
|
|
1016
|
-
setOpenThePicker(true);
|
|
1017
|
-
settled = true;
|
|
1018
|
-
return true;
|
|
1019
|
-
}
|
|
1020
|
-
if (findRowFocusTarget(editor, { textEntryOnly: true }) !== control) {
|
|
1021
|
-
// Not a text box: hold it until one arrives (see `provisional`).
|
|
1022
|
-
provisional = control;
|
|
1023
|
-
return false;
|
|
1024
|
-
}
|
|
1025
|
-
settled = true;
|
|
1026
|
-
return true;
|
|
1027
|
-
};
|
|
1028
|
-
if (takeCaret() || typeof MutationObserver === 'undefined') return undefined;
|
|
1029
|
-
const observer = new MutationObserver(() => {
|
|
1030
|
-
if (takeCaret()) observer.disconnect();
|
|
1031
|
-
});
|
|
1032
|
-
// Attributes too: an editor's text box can become focusable by an attribute or
|
|
1033
|
-
// style change after it is inserted (see the `focus()` checks above).
|
|
1034
|
-
observer.observe(editor, { childList: true, subtree: true, attributes: true });
|
|
1035
|
-
return () => observer.disconnect();
|
|
1036
|
-
}, [isExpanded, optionName, inlineEditable]);
|
|
1037
|
-
|
|
1038
|
-
// What the field held when it was opened — the baseline Cancel restores.
|
|
1039
|
-
//
|
|
1040
|
-
// Deliberately NOT `originalValue`, which is the whole form's load-time
|
|
1041
|
-
// snapshot. That answers "undo every change ever made to this field", which
|
|
1042
|
-
// is a different question from "forget what I just typed"; a Cancel sitting
|
|
1043
|
-
// next to Done has to mean the second one, or it silently throws away edits
|
|
1044
|
-
// the user made earlier and had already accepted.
|
|
1045
|
-
//
|
|
1046
|
-
// The dependency list is `isExpanded` alone on purpose: adding the value
|
|
1047
|
-
// would re-take the snapshot on every keystroke, leaving nothing to cancel.
|
|
1048
|
-
const editEntryValue = React.useRef<{ value: any; type?: string } | undefined>(undefined);
|
|
1049
|
-
React.useEffect(() => {
|
|
1050
|
-
if (isExpanded) {
|
|
1051
|
-
editEntryValue.current = { value: optionField?.value, type: optionField?.type };
|
|
1052
|
-
} else {
|
|
1053
|
-
editEntryValue.current = undefined;
|
|
1054
|
-
}
|
|
1055
|
-
}, [isExpanded]);
|
|
1056
|
-
|
|
1057
|
-
// True while the open editor holds something other than what it opened with.
|
|
1058
|
-
const editedSinceOpened =
|
|
1059
|
-
isExpanded &&
|
|
1060
|
-
!!editEntryValue.current &&
|
|
1061
|
-
!isEqual(editEntryValue.current.value, optionField?.value);
|
|
1062
|
-
|
|
1063
|
-
// Discard whatever was typed since the field was opened and close it. Used
|
|
1064
|
-
// by both the Cancel button and Escape, so the two cannot diverge.
|
|
1065
|
-
const cancelEdit = React.useCallback(() => {
|
|
1066
|
-
const snapshot = editEntryValue.current;
|
|
1067
|
-
if (snapshot && !isEqual(snapshot.value, optionField?.value)) {
|
|
1068
|
-
handleValueChange(optionName, snapshot.value, snapshot.type);
|
|
1069
|
-
}
|
|
1070
|
-
toggleExpandedOption(optionName);
|
|
1071
|
-
}, [handleValueChange, optionField?.value, optionName, toggleExpandedOption]);
|
|
1072
|
-
|
|
1073
|
-
// The sub-schema describing this field's value, when it has one. A value the
|
|
1074
|
-
// form knows the shape of is previewed THROUGH that shape (see
|
|
1075
|
-
// `SchemaDataView`); only genuinely undescribed data falls through to the
|
|
1076
|
-
// untyped data tree.
|
|
1077
|
-
//
|
|
1078
|
-
// The server does NOT inline that sub-schema. `encodeFieldsForUi()` registers
|
|
1079
|
-
// it and sends an id string in its place, which the consumer fetches back —
|
|
1080
|
-
// exactly what AutoFormField does to build the editor ("Some arg schemas are
|
|
1081
|
-
// not provided as objects, but as strings so we need to fetch them"). A
|
|
1082
|
-
// preview that understood only the inline object therefore fell through to
|
|
1083
|
-
// the untyped tree for every field a real server describes: the schema view
|
|
1084
|
-
// worked against hand-written story fixtures and nowhere else.
|
|
1085
|
-
//
|
|
1086
|
-
// Resolved here rather than beside the preview it feeds: the preview renders
|
|
1087
|
-
// below an early return, so a hook placed there would run conditionally.
|
|
1088
|
-
const rawArgSchema = (schema as { arg_schema?: IQorusFormSchema | string } | undefined)
|
|
1089
|
-
?.arg_schema;
|
|
1090
|
-
const argSchemaId = typeof rawArgSchema === 'string' ? rawArgSchema : undefined;
|
|
1091
|
-
const [resolvedArgSchema, setResolvedArgSchema] = React.useState<IQorusFormSchema | undefined>(
|
|
1092
|
-
undefined
|
|
1093
|
-
);
|
|
1094
|
-
|
|
1095
|
-
// Only worth a request when there is a described value to preview: a row whose
|
|
1096
|
-
// value is unset or scalar renders no inset, so fetching its schema would buy
|
|
1097
|
-
// a round trip per row and show nothing. `query` caches GETs, so rows sharing
|
|
1098
|
-
// an id (every row of one list) cost one request between them.
|
|
1099
|
-
const valueLooksStructured = !!optionField?.value && typeof optionField.value === 'object';
|
|
1100
|
-
|
|
1101
|
-
React.useEffect(() => {
|
|
1102
|
-
if (!argSchemaId || !valueLooksStructured) {
|
|
1103
|
-
return undefined;
|
|
1104
|
-
}
|
|
1105
|
-
let cancelled = false;
|
|
1106
|
-
(async () => {
|
|
1107
|
-
const response = await queryUnlessCancelled<IQorusFormSchema>({
|
|
1108
|
-
url: `dataprovider/arg_schemas/${argSchemaId}`,
|
|
1109
|
-
method: 'GET',
|
|
1110
|
-
});
|
|
1111
|
-
// A failed fetch is not an error state here: the preview simply stays the
|
|
1112
|
-
// untyped tree, which is what it was before the schema view existed. Nor
|
|
1113
|
-
// is an abandoned one (no response).
|
|
1114
|
-
if (!cancelled && response?.ok) {
|
|
1115
|
-
setResolvedArgSchema(response.data as IQorusFormSchema);
|
|
1116
|
-
}
|
|
1117
|
-
})();
|
|
1118
|
-
return () => {
|
|
1119
|
-
cancelled = true;
|
|
1120
|
-
};
|
|
1121
|
-
}, [argSchemaId, valueLooksStructured]);
|
|
1122
|
-
|
|
1123
|
-
const previewArgSchema =
|
|
1124
|
-
argSchemaId ? resolvedArgSchema : (rawArgSchema as IQorusFormSchema | undefined);
|
|
1125
|
-
const previewWithSchema = React.useMemo(
|
|
1126
|
-
() => canRenderWithSchema(optionField?.value, previewArgSchema),
|
|
1127
|
-
[optionField?.value, previewArgSchema]
|
|
1128
|
-
);
|
|
1129
|
-
|
|
1130
|
-
// The form-load revert: back to the value the FORM was loaded with.
|
|
1131
|
-
//
|
|
1132
|
-
// In an open editor this is offered only when it would do something
|
|
1133
|
-
// `cancelEditButton` does not — that is, when the field was already
|
|
1134
|
-
// modified BEFORE it was opened, so "undo this edit" and "undo every edit"
|
|
1135
|
-
// are different answers. On the usual path (open an untouched field, type,
|
|
1136
|
-
// change your mind) the two are identical, and two buttons with the same
|
|
1137
|
-
// effect and different names are worse than one.
|
|
1138
|
-
//
|
|
1139
|
-
// It is unconditional on the READ row, where it renders inline in the
|
|
1140
|
-
// status-dot column — there is no edit in progress there to cancel, so the
|
|
1141
|
-
// whole-field revert is the only undo that makes sense.
|
|
1142
|
-
const revertButton =
|
|
1143
|
-
changed ?
|
|
1144
|
-
<ReqoreButton
|
|
1145
|
-
className='options-readfirst-revert'
|
|
1146
|
-
size='small'
|
|
1147
|
-
flat
|
|
1148
|
-
minimal
|
|
1149
|
-
icon='HistoryLine'
|
|
1150
|
-
tooltip='Revert changes'
|
|
1151
|
-
onClick={(e: React.MouseEvent) => {
|
|
1152
|
-
e.stopPropagation();
|
|
1153
|
-
handleValueChange(
|
|
1154
|
-
optionName,
|
|
1155
|
-
originalValue.current?.[optionName]?.value,
|
|
1156
|
-
originalValue.current?.[optionName]?.type
|
|
1157
|
-
);
|
|
1158
|
-
}}
|
|
1159
|
-
/>
|
|
1160
|
-
: null;
|
|
1161
|
-
// Clear-value: empties a set field (keeps the field; the value goes blank)
|
|
1162
|
-
// — distinct from the read-row "Remove field" (which deletes an optional
|
|
1163
|
-
// field entirely). Once cleared, `changed` flips on and `hasValue` off, so
|
|
1164
|
-
// this button gives way to `revertButton` in the same slot — the morph the
|
|
1165
|
-
// edit row's actions are designed around. Clearing is destructive even
|
|
1166
|
-
// though it can be reverted before saving, so both the trigger and its
|
|
1167
|
-
// confirmation use danger intent.
|
|
1168
|
-
//
|
|
1169
|
-
// Only editors with NO built-in clear of their own get this button —
|
|
1170
|
-
// toggles and fixed-choice pickers. The text/number/date inputs already
|
|
1171
|
-
// render ReqoreInput's ✕ (which itself trips `changed`, so `revertButton`
|
|
1172
|
-
// takes over once emptied), so adding ours there would double the ✕.
|
|
1173
|
-
const editorLacksOwnClear =
|
|
1174
|
-
editType === 'bool' ||
|
|
1175
|
-
editType === 'boolean' ||
|
|
1176
|
-
// Non-creatable allowed_values render a radio/select (no input clear); a
|
|
1177
|
-
// CREATABLE one still renders the raw input (with its own ✕), so it keeps
|
|
1178
|
-
// its clear — matching the dispatch in AutoFormField/TemplateField.
|
|
1179
|
-
(!!size(schema?.allowed_values) && !schema?.allowed_values_creatable);
|
|
1180
|
-
const confirmClearValue = (event?: React.MouseEvent) => {
|
|
1181
|
-
event?.stopPropagation();
|
|
1182
|
-
confirmAction({
|
|
1183
|
-
title: 'Clear value',
|
|
1184
|
-
description: `Clear the current value of "${label}"?`,
|
|
1185
|
-
confirmLabel: 'Clear value',
|
|
1186
|
-
confirmButtonIntent: 'danger',
|
|
1187
|
-
intent: 'danger',
|
|
1188
|
-
onConfirm: () => handleValueChange(optionName, undefined),
|
|
1189
|
-
});
|
|
1190
|
-
};
|
|
1191
|
-
const clearValueButton =
|
|
1192
|
-
hasValue && !readOnly && editorLacksOwnClear ?
|
|
1193
|
-
<ReqoreButton
|
|
1194
|
-
className='options-readfirst-clear'
|
|
1195
|
-
size='small'
|
|
1196
|
-
flat
|
|
1197
|
-
minimal
|
|
1198
|
-
icon='DeleteBinLine'
|
|
1199
|
-
intent='danger'
|
|
1200
|
-
aria-label='Clear value'
|
|
1201
|
-
tooltip='Clear value'
|
|
1202
|
-
onClick={confirmClearValue}
|
|
1203
|
-
/>
|
|
1204
|
-
: null;
|
|
1205
|
-
// Batched commit: a changed row is a draft until Save — mark it with the
|
|
1206
|
-
// product's Draft chip (always visible, unlike the hover-revealed revert).
|
|
1207
|
-
const draftChip =
|
|
1208
|
-
commitMode === 'batched' && changed ?
|
|
1209
|
-
<ReqoreTag
|
|
1210
|
-
className='options-readfirst-draft'
|
|
1211
|
-
label='Draft'
|
|
1212
|
-
intent='warning'
|
|
1213
|
-
icon='EditLine'
|
|
1214
|
-
size='tiny'
|
|
1215
|
-
minimal
|
|
1216
|
-
fixed
|
|
1217
|
-
flat
|
|
1218
|
-
compact
|
|
1219
|
-
effect={calm ? undefined : { uppercase: true, spaced: 1 }}
|
|
1220
|
-
/>
|
|
1221
|
-
: null;
|
|
1222
|
-
const isPassiveCloseAction = fixedAllowedValueOption;
|
|
1223
|
-
// Cancel — the counterpart to Done, and the answer to "how do I get out of
|
|
1224
|
-
// here without keeping this". Done was the only way to leave an open field,
|
|
1225
|
-
// so every way out committed: the check, clicking away, and Escape (which
|
|
1226
|
-
// closed without discarding, so it committed too, silently).
|
|
1227
|
-
//
|
|
1228
|
-
// Shown only while there is something to discard. On an untouched field
|
|
1229
|
-
// Cancel and Done would do exactly the same thing, and two buttons that
|
|
1230
|
-
// differ in name but not in effect are worse than one.
|
|
1231
|
-
//
|
|
1232
|
-
// Not offered when the close action is passive (a fixed choice) —
|
|
1233
|
-
// nothing can have been typed, so there is nothing to cancel.
|
|
1234
|
-
const cancelEditButton =
|
|
1235
|
-
!isPassiveCloseAction && editedSinceOpened ?
|
|
1236
|
-
<ReqoreButton
|
|
1237
|
-
className='options-readfirst-cancel'
|
|
1238
|
-
size='small'
|
|
1239
|
-
flat
|
|
1240
|
-
minimal
|
|
1241
|
-
fixed
|
|
1242
|
-
icon='CloseLine'
|
|
1243
|
-
intent={calm ? undefined : 'warning'}
|
|
1244
|
-
// "Cancel edit", not "Cancel": a bare Cancel collides with the one
|
|
1245
|
-
// in Reqore's confirmation modal, and this one is specifically about
|
|
1246
|
-
// abandoning the edit rather than dismissing a dialog.
|
|
1247
|
-
aria-label='Cancel edit'
|
|
1248
|
-
tooltip='Cancel edit — discard the changes made since this field was opened'
|
|
1249
|
-
onClick={(event: React.MouseEvent) => {
|
|
1250
|
-
event.stopPropagation();
|
|
1251
|
-
cancelEdit();
|
|
1252
|
-
}}
|
|
1253
|
-
/>
|
|
1254
|
-
: null;
|
|
1255
|
-
// See `revertButton` above for why this is conditional in the edit row.
|
|
1256
|
-
const openedWithFormLoadValue =
|
|
1257
|
-
!editEntryValue.current ||
|
|
1258
|
-
isEqual(editEntryValue.current.value, originalValue.current?.[optionName]?.value);
|
|
1259
|
-
const editRowRevertButton = openedWithFormLoadValue ? null : revertButton;
|
|
1260
|
-
const closeExpandedFieldButton = (
|
|
1261
|
-
<ReqoreButton
|
|
1262
|
-
className='options-readfirst-done'
|
|
1263
|
-
size='small'
|
|
1264
|
-
// Calm: a neutral button. Closing a field is not a healthy figure and
|
|
1265
|
-
// not the page's one primary action, so it takes neither colour.
|
|
1266
|
-
intent={!isPassiveCloseAction && !calm ? 'success' : undefined}
|
|
1267
|
-
flat={isPassiveCloseAction}
|
|
1268
|
-
minimal={isPassiveCloseAction}
|
|
1269
|
-
fixed
|
|
1270
|
-
icon={isPassiveCloseAction ? 'CloseLine' : 'CheckLine'}
|
|
1271
|
-
aria-label={isPassiveCloseAction ? 'Close field' : 'Done'}
|
|
1272
|
-
tooltip={isPassiveCloseAction ? 'Close field' : 'Done'}
|
|
1273
|
-
onClick={() => toggleExpandedOption(optionName)}
|
|
1274
|
-
/>
|
|
1275
|
-
);
|
|
1276
|
-
|
|
1277
|
-
// Info tiers: Tier 1 (danger/warning + dependency hints) must be visible
|
|
1278
|
-
// without interaction; Tier 2 (info/success, default notes) sits behind ⓘ.
|
|
1279
|
-
const infoActive = !hidden;
|
|
1280
|
-
type TInfoMsg = { intent?: string; title?: string; content: string };
|
|
1281
|
-
const schemaMessages: TInfoMsg[] =
|
|
1282
|
-
infoActive ?
|
|
1283
|
-
(getShownSchemaMessages((schema as any)?.messages as any[], availableOptions, options).map(
|
|
1284
|
-
(m) => ({
|
|
1285
|
-
intent: m.intent,
|
|
1286
|
-
title: m.title,
|
|
1287
|
-
content: m.content,
|
|
1288
|
-
})
|
|
1289
|
-
) as TInfoMsg[])
|
|
1290
|
-
: [];
|
|
1291
|
-
// A row states what is wrong with what it holds, read-only or not: "This
|
|
1292
|
-
// field is required" under an empty required field, a value that is not a
|
|
1293
|
-
// choice, an unmet dependency. A read-only reader is often the one who has
|
|
1294
|
-
// to get the record fixed, and a view that hides why it is incomplete
|
|
1295
|
-
// leaves them guessing. The value slot of an empty field keeps its plain
|
|
1296
|
-
// "—" (or the default it falls back to), so the message is the only place
|
|
1297
|
-
// the requirement is said: once, and the same way in both modes.
|
|
1298
|
-
const fieldMessages: TInfoMsg[] =
|
|
1299
|
-
infoActive ?
|
|
1300
|
-
getOptionFieldMessages({
|
|
1301
|
-
schema: options || {},
|
|
1302
|
-
option: optionField || ({} as IQorusFormField),
|
|
1303
|
-
name: optionName,
|
|
1304
|
-
allOptions: availableOptions,
|
|
1305
|
-
getType: getTypeForOption,
|
|
1306
|
-
isRendererOnly,
|
|
1307
|
-
calm,
|
|
1308
|
-
readOnly,
|
|
1309
|
-
// Until the person has been to the field, its requirement is stated plainly.
|
|
1310
|
-
requirementPending: !visited,
|
|
1311
|
-
}).map((m) => ({ intent: m.intent as string, content: String(m.label) }))
|
|
1312
|
-
: [];
|
|
1313
|
-
// TWO display channels, matching the Focus prototype:
|
|
1314
|
-
// • dedicated schema `messages` → prominent coloured PANELS below the row;
|
|
1315
|
-
// • everything else (validation reasons, the unmet-dependency hint, the
|
|
1316
|
-
// default-value note) → a compact INLINE reason strip — not a panel.
|
|
1317
|
-
const panelMessages = schemaMessages;
|
|
1318
|
-
const inlineMessages: TInfoMsg[] = [
|
|
1319
|
-
...fieldMessages,
|
|
1320
|
-
...(infoActive && schema?.default_value_desc ?
|
|
1321
|
-
[
|
|
1322
|
-
{
|
|
1323
|
-
content:
|
|
1324
|
-
`Default: ${schema.default_value_display_name || ''} — ${schema.default_value_desc}`.trim(),
|
|
1325
|
-
},
|
|
1326
|
-
]
|
|
1327
|
-
: []),
|
|
1328
|
-
];
|
|
1329
|
-
const allMsgs = [...panelMessages, ...inlineMessages];
|
|
1330
|
-
const worstIntent =
|
|
1331
|
-
allMsgs.some((m) => m.intent === 'danger') ? 'danger'
|
|
1332
|
-
: allMsgs.some((m) => m.intent === 'warning') ? 'warning'
|
|
1333
|
-
: undefined;
|
|
1334
|
-
const intentColor =
|
|
1335
|
-
worstIntent === 'danger' ? cDanger
|
|
1336
|
-
: worstIntent === 'warning' ? cWarning
|
|
1337
|
-
: undefined;
|
|
1338
|
-
const showStripe = infoActive && !!intentColor;
|
|
1339
|
-
const labelShortDesc = schema?.short_desc;
|
|
1340
|
-
// The per-row ⓘ is gone: short_desc shows under the name only when the global
|
|
1341
|
-
// "descriptions" toggle is engaged (and inside the editor when expanded).
|
|
1342
|
-
const showLabelDesc = !!labelShortDesc && showAllDescriptions === true;
|
|
1343
|
-
|
|
1344
|
-
// The `?` that opens the long-form help dialog. Shared by the read row AND
|
|
1345
|
-
// the inline editor: opening a field used to drop it, leaving an editing row
|
|
1346
|
-
// with no route to the field's description at all. Stops propagation because
|
|
1347
|
-
// both labels are click targets themselves (the row expands / collapses).
|
|
1348
|
-
const helpIcon =
|
|
1349
|
-
fieldHasLongDescription(schema) ?
|
|
1350
|
-
<ReqoreIcon
|
|
1351
|
-
icon='QuestionLine'
|
|
1352
|
-
size='12px'
|
|
1353
|
-
effect={{ opacity: 0.55 }}
|
|
1354
|
-
margin='left'
|
|
1355
|
-
marginSize='tiny'
|
|
1356
|
-
role='button'
|
|
1357
|
-
tabIndex={-1}
|
|
1358
|
-
aria-label='Help'
|
|
1359
|
-
className='options-readfirst-help'
|
|
1360
|
-
style={{ cursor: 'help' }}
|
|
1361
|
-
onClick={(event) => {
|
|
1362
|
-
event.stopPropagation();
|
|
1363
|
-
handleOptionLabelClick(optionName);
|
|
1364
|
-
}}
|
|
1365
|
-
/>
|
|
1366
|
-
: null;
|
|
1367
|
-
|
|
1368
|
-
// The gap rides on the message itself rather than on the panel around it:
|
|
1369
|
-
// the panel is a vertical ReqoreControlGroup, which owns its own layout and
|
|
1370
|
-
// stacks children flush, and the class rule that used to carry a 4px gap
|
|
1371
|
-
// never reached this element at all — which is why two messages read as one
|
|
1372
|
-
// two-tone block with no space between them.
|
|
1373
|
-
/* A message is PROSE, so it is drawn by the host's prose renderer.
|
|
1374
|
-
|
|
1375
|
-
`markdownRendererContext` says this package renders markdown "in exactly
|
|
1376
|
-
one place — a field's description". That was one place short: a schema
|
|
1377
|
-
message is written by the same author, in the same dialect, and it landed
|
|
1378
|
-
here as a raw string. A Qorus warning reading "Give that step a **Fixture
|
|
1379
|
-
Output**" showed the asterisks, and every reference inside it stayed a raw
|
|
1380
|
-
`$.` path where the editor two lines above renders it as a named chip.
|
|
1381
|
-
|
|
1382
|
-
`compact` for the same reason the row inset asks for it: this is a strip
|
|
1383
|
-
inside a row, never a page, and markdown authored as a document opens with
|
|
1384
|
-
a heading that would outgrow the field label above it. Without a host
|
|
1385
|
-
renderer the string is drawn as it always was. */
|
|
1386
|
-
const renderInfoStrip = (m: TInfoMsg, index: number) => (
|
|
1387
|
-
<ReqoreMessage
|
|
1388
|
-
key={`${m.content}-${index}`}
|
|
1389
|
-
size='small'
|
|
1390
|
-
opaque={false}
|
|
1391
|
-
flat
|
|
1392
|
-
// Calm: a note that only informs takes no colour — blue is kept for "a
|
|
1393
|
-
// filter is on", green for a healthy figure. A warning or a failure
|
|
1394
|
-
// keeps its intent: those are what a person has to act on.
|
|
1395
|
-
intent={
|
|
1396
|
-
(calm && (!m.intent || m.intent === 'info' || m.intent === 'success') ?
|
|
1397
|
-
undefined
|
|
1398
|
-
: m.intent) as never
|
|
1399
|
-
}
|
|
1400
|
-
title={m.title}
|
|
1401
|
-
style={{ marginBottom: 8 }}
|
|
1402
|
-
>
|
|
1403
|
-
{markdownRenderer && typeof m.content === 'string' ?
|
|
1404
|
-
markdownRenderer({ value: m.content, compact: true })
|
|
1405
|
-
: m.content}
|
|
1406
|
-
</ReqoreMessage>
|
|
1407
|
-
);
|
|
1408
|
-
const reasonColor = (intent?: string) =>
|
|
1409
|
-
intent === 'danger' ? cDanger
|
|
1410
|
-
: intent === 'warning' ? cWarning
|
|
1411
|
-
: intent === 'success' ?
|
|
1412
|
-
(theme?.intents as Record<string, string> | undefined)?.success || cInfo
|
|
1413
|
-
: `${cMuted}cc`;
|
|
1414
|
-
|
|
1415
|
-
const infoToggle = null;
|
|
1416
|
-
|
|
1417
|
-
// Cluster (required-group connection) — shared by the read row, its block
|
|
1418
|
-
// wrapper AND the inline editor, so the rail/node/highlight persist across
|
|
1419
|
-
// view↔edit. The node is opaque (it masks the rail behind it); hovering any
|
|
1420
|
-
// member lights up the whole group via the shared highlight state.
|
|
1421
|
-
const clusterGroup =
|
|
1422
|
-
clustered ? (schema?.required_groups as string[] | undefined)?.[0] : undefined;
|
|
1423
|
-
const clusterMembers = clusterGroup ? requiredGroupsInfo.members[clusterGroup] || [] : [];
|
|
1424
|
-
// The group is fulfilled once any member is set — the whole rail then reads
|
|
1425
|
-
// success (not just the satisfying node).
|
|
1426
|
-
const clusterSatisfied = !!(clusterGroup && requiredGroupsInfo.satisfiedBy[clusterGroup]);
|
|
1427
|
-
const clusterBlockClass =
|
|
1428
|
-
clustered ?
|
|
1429
|
-
`readfirst-cluster-rail${clusterFirst ? ' readfirst-cluster-first' : ''}${clusterLast ? ' readfirst-cluster-last' : ''}${clusterSatisfied ? ' readfirst-cluster-satisfied' : ''}`
|
|
1430
|
-
: '';
|
|
1431
|
-
// The connection rail + status nodes are gone — the "One of the below is
|
|
1432
|
-
// required" box (and the Covers / Covered-by chips) carry the grouping now.
|
|
1433
|
-
const clusterNode = null;
|
|
1434
|
-
const clusterHoverProps =
|
|
1435
|
-
clustered && clusterMembers.length ?
|
|
1436
|
-
{
|
|
1437
|
-
onMouseEnter: () => setHighlightedOptions(clusterMembers),
|
|
1438
|
-
onMouseLeave: () => setHighlightedOptions([]),
|
|
1439
|
-
}
|
|
1440
|
-
: {};
|
|
1441
|
-
|
|
1442
|
-
// Items the EDITOR inside this row published — see `rowMenuContext`.
|
|
1443
|
-
const { rowMenu, items: editorMenuItems } = useRowMenuRegistry();
|
|
1444
|
-
|
|
1445
|
-
// Secondary edit actions tuck into a "More" (⋮) menu so the card header stays
|
|
1446
|
-
// calm: Fullscreen always, plus Remove field for a removable option. Rendered
|
|
1447
|
-
// just before the Done ✓ in both expanded layouts.
|
|
1448
|
-
const moreMenu = (
|
|
1449
|
-
<ReqoreDropdown
|
|
1450
|
-
className='options-readfirst-more'
|
|
1451
|
-
icon='More2Fill'
|
|
1452
|
-
flat
|
|
1453
|
-
minimal
|
|
1454
|
-
fixed
|
|
1455
|
-
size='small'
|
|
1456
|
-
tooltip='More actions'
|
|
1457
|
-
items={[
|
|
1458
|
-
{
|
|
1459
|
-
label: 'Edit fullscreen',
|
|
1460
|
-
icon: 'FullscreenLine',
|
|
1461
|
-
onClick: () => setFocusedEditing(optionName),
|
|
1462
|
-
} as IReqoreDropdownItem,
|
|
1463
|
-
...(removable && !hidden ?
|
|
1464
|
-
[
|
|
1465
|
-
{
|
|
1466
|
-
label: 'Remove field',
|
|
1467
|
-
icon: 'DeleteBinLine',
|
|
1468
|
-
intent: 'danger',
|
|
1469
|
-
onClick: () =>
|
|
1470
|
-
confirmAction({
|
|
1471
|
-
title: 'Remove field',
|
|
1472
|
-
onConfirm: () => removeSelectedOption(optionName),
|
|
1473
|
-
}),
|
|
1474
|
-
} as IReqoreDropdownItem,
|
|
1475
|
-
]
|
|
1476
|
-
: []),
|
|
1477
|
-
// Consumer-injected actions that did not fit inline (or collapsed
|
|
1478
|
-
// wholesale on touch) land here — this row already has a menu, so they
|
|
1479
|
-
// reuse it rather than adding a second one beside it.
|
|
1480
|
-
...injectedOptionActionMenuItems,
|
|
1481
|
-
// ...and for the same reason, so do the editor's own actions. Without
|
|
1482
|
-
// this an editor with affordances of its own had to draw a second ⋮
|
|
1483
|
-
// inside the value cell, beside this one.
|
|
1484
|
-
...editorMenuItems,
|
|
1485
|
-
]}
|
|
1486
|
-
/>
|
|
1487
|
-
);
|
|
1488
|
-
|
|
1489
|
-
// The absorbed siblings' controls, rendered inside this row.
|
|
1490
|
-
//
|
|
1491
|
-
// Each keeps its own label so it is still identifiable — it is a different
|
|
1492
|
-
// field, not a property of this one — but it sits inside this row's
|
|
1493
|
-
// container, above the editor, so the pair reads as the single decision it
|
|
1494
|
-
// is. `renderOption` is the same function the row uses for its own editor,
|
|
1495
|
-
// so an absorbed field renders exactly as it would have on its own row.
|
|
1496
|
-
const absorbedNodes =
|
|
1497
|
-
absorbedFields?.length ?
|
|
1498
|
-
absorbedFields
|
|
1499
|
-
.map((absorbedName) => {
|
|
1500
|
-
const absorbedField = availableOptions?.[absorbedName] as IQorusFormField | undefined;
|
|
1501
|
-
if (!absorbedField) return null;
|
|
1502
|
-
return (
|
|
1503
|
-
// No label element of its own: the editor now heads itself with
|
|
1504
|
-
// the field's name (see FieldAllowedValuesCheckGroup), so a label
|
|
1505
|
-
// beside it printed the same word twice.
|
|
1506
|
-
<StyledAbsorbedField key={absorbedName} className='options-readfirst-absorbed'>
|
|
1507
|
-
<div style={{ minWidth: 0, flex: 1 }}>
|
|
1508
|
-
{renderOption(absorbedName, absorbedField, 'small', true)}
|
|
1509
|
-
</div>
|
|
1510
|
-
</StyledAbsorbedField>
|
|
1511
|
-
);
|
|
1512
|
-
})
|
|
1513
|
-
.filter(Boolean)
|
|
1514
|
-
: null;
|
|
1515
|
-
|
|
1516
|
-
// Declared here rather than beside the hash rows below: the `isExpanded`
|
|
1517
|
-
// branch returns before that point, and its editor needs to know whether it is
|
|
1518
|
-
// holding code.
|
|
1519
|
-
const valueType = getValueType(optionField, schema);
|
|
1520
|
-
|
|
1521
|
-
// How much source there is describes the FIELD, not its value, so the count
|
|
1522
|
-
// sits under the field's name — the same rule `lineCountNote` already
|
|
1523
|
-
// follows for a multi-line markdown value, and the same reason: the value
|
|
1524
|
-
// column carries the value and nothing else. It renders on the read row and
|
|
1525
|
-
// the open one alike, because "how much code is here?" is a question that
|
|
1526
|
-
// outlives opening the editor.
|
|
1527
|
-
//
|
|
1528
|
-
// It is a plain monospaced note rather than a tag, and deliberately the same
|
|
1529
|
-
// one `lineCountNote` renders: both answer "how big is this field's value?"
|
|
1530
|
-
// in the same corner of the same column, so stating one as a chip and the
|
|
1531
|
-
// other as text made two spellings of one idea.
|
|
1532
|
-
const codeSizeNode =
|
|
1533
|
-
(
|
|
1534
|
-
valueType === 'code-editor' &&
|
|
1535
|
-
typeof optionField?.value === 'string' &&
|
|
1536
|
-
optionField.value !== ''
|
|
1537
|
-
) ?
|
|
1538
|
-
(() => {
|
|
1539
|
-
const { lines, chars } = describeCodeSize(optionField.value as string);
|
|
1540
|
-
return (
|
|
1541
|
-
<ReqoreP
|
|
1542
|
-
className='options-readfirst-label-code-size'
|
|
1543
|
-
size='tiny'
|
|
1544
|
-
effect={{ opacity: 0.45, textAlign: 'left' }}
|
|
1545
|
-
style={{ fontFamily: 'monospace' }}
|
|
1546
|
-
>
|
|
1547
|
-
{`${formatCodeChars(chars)} ${formatCodeLines(lines)}`}
|
|
1548
|
-
</ReqoreP>
|
|
1549
|
-
);
|
|
1550
|
-
})()
|
|
1551
|
-
: null;
|
|
1552
|
-
|
|
1553
|
-
if (isExpanded && !readOnly) {
|
|
1554
|
-
if (inlineEditable) {
|
|
1555
|
-
const collapse = () => toggleExpandedOption(optionName);
|
|
1556
|
-
const editingRow = (
|
|
1557
|
-
<div
|
|
1558
|
-
key={optionName}
|
|
1559
|
-
ref={rowRef}
|
|
1560
|
-
data-field={optionName}
|
|
1561
|
-
className='readfirst-row readfirst-row-editing options-readfirst-inline options-readfirst-value'
|
|
1562
|
-
style={
|
|
1563
|
-
readRowHeights.current[optionName] ?
|
|
1564
|
-
{ minHeight: readRowHeights.current[optionName] }
|
|
1565
|
-
: undefined
|
|
1566
|
-
}
|
|
1567
|
-
{...clusterHoverProps}
|
|
1568
|
-
>
|
|
1569
|
-
<StyledLabelBlock className='options-readfirst-label-block'>
|
|
1570
|
-
<StyledRowLabel
|
|
1571
|
-
role='button'
|
|
1572
|
-
tabIndex={0}
|
|
1573
|
-
aria-label={`Collapse ${label}`}
|
|
1574
|
-
title={schema?.short_desc || undefined}
|
|
1575
|
-
$color={cLabel}
|
|
1576
|
-
$pointer
|
|
1577
|
-
onClick={collapse}
|
|
1578
|
-
onKeyDown={(event) => {
|
|
1579
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
1580
|
-
event.preventDefault();
|
|
1581
|
-
collapse();
|
|
1582
|
-
}
|
|
1583
|
-
}}
|
|
1584
|
-
>
|
|
1585
|
-
{label}
|
|
1586
|
-
{required && !calm ?
|
|
1587
|
-
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1588
|
-
: null}
|
|
1589
|
-
{helpIcon}
|
|
1590
|
-
</StyledRowLabel>
|
|
1591
|
-
{/* An OPEN field always shows its short_desc — that's where the hint
|
|
1592
|
-
is needed and there's no per-row button to reveal it (the edit
|
|
1593
|
-
card does the same). The global descriptions toggle governs READ
|
|
1594
|
-
rows only, so collapsing the field is what hides it again. */}
|
|
1595
|
-
{labelShortDesc ?
|
|
1596
|
-
<StyledLabelDesc
|
|
1597
|
-
className='options-readfirst-label-desc'
|
|
1598
|
-
size='small'
|
|
1599
|
-
effect={{ opacity: 0.55 }}
|
|
1600
|
-
>
|
|
1601
|
-
{labelShortDesc}
|
|
1602
|
-
</StyledLabelDesc>
|
|
1603
|
-
: null}
|
|
1604
|
-
{codeSizeNode}
|
|
1605
|
-
</StyledLabelBlock>
|
|
1606
|
-
<div
|
|
1607
|
-
ref={editorRef}
|
|
1608
|
-
className='options-readfirst-row-editor'
|
|
1609
|
-
style={{ minWidth: 0 }}
|
|
1610
|
-
onKeyDown={(event) => {
|
|
1611
|
-
if (event.key === 'Escape') {
|
|
1612
|
-
event.stopPropagation();
|
|
1613
|
-
// Escape discards. It used to call `collapse()`, which closed
|
|
1614
|
-
// the field and KEPT whatever had been typed — the one
|
|
1615
|
-
// keystroke every editor treats as "get me out of this
|
|
1616
|
-
// without saving" was the quietest way to commit.
|
|
1617
|
-
cancelEdit();
|
|
1618
|
-
}
|
|
1619
|
-
}}
|
|
1620
|
-
>
|
|
1621
|
-
{/* The size chip survives into the editor.
|
|
1622
|
-
|
|
1623
|
-
Opening a field replaces the whole value cell with its editor, which
|
|
1624
|
-
for every other type is right — the editor shows the value, so a
|
|
1625
|
-
summary of it above would say the same thing twice. Code is the
|
|
1626
|
-
exception: the chip counts lines and characters, which the editor
|
|
1627
|
-
does not show anywhere. Dropping it lost real information AND moved
|
|
1628
|
-
everything under it up by the height of a chip, so the editor
|
|
1629
|
-
appeared to jump as it opened.
|
|
1630
|
-
|
|
1631
|
-
It renders here, at the top of the value cell, which is exactly where
|
|
1632
|
-
it sits on the read row — so the switch moves nothing. */}
|
|
1633
|
-
{/* The field's own schema messages follow it into the editor.
|
|
1634
|
-
|
|
1635
|
-
A message is guidance about the value, so the moment it matters
|
|
1636
|
-
most is while the value is being changed — and a diagnostic is
|
|
1637
|
-
useless anywhere else: it names a line the author can only act
|
|
1638
|
-
on with the editor in front of them. They used to render on the
|
|
1639
|
-
READ row alone, so opening the field to fix what one said was
|
|
1640
|
-
what made it disappear. Worse for a host that opens a field on
|
|
1641
|
-
arrival: the message was never visible at all.
|
|
1642
|
-
|
|
1643
|
-
Above the editor rather than below it, and above the absorbed
|
|
1644
|
-
siblings, because it is the reason the author opened the row. */}
|
|
1645
|
-
{panelMessages.length ?
|
|
1646
|
-
<ReqoreControlGroup
|
|
1647
|
-
className='options-readfirst-info-panel'
|
|
1648
|
-
vertical
|
|
1649
|
-
fluid
|
|
1650
|
-
gapSize='normal'
|
|
1651
|
-
>
|
|
1652
|
-
{panelMessages.map(renderInfoStrip)}
|
|
1653
|
-
</ReqoreControlGroup>
|
|
1654
|
-
: null}
|
|
1655
|
-
{/* Absorbed siblings sit at the TOP of the value cell, above the
|
|
1656
|
-
editor they belong to — the language you are writing in is
|
|
1657
|
-
read before the code, not after it. */}
|
|
1658
|
-
{absorbedNodes}
|
|
1659
|
-
{/* The editor may publish into THIS row's ⋮ — see `rowMenuContext`.
|
|
1660
|
-
Only the editors rendered in the row are given the channel: the
|
|
1661
|
-
fullscreen modal below has no row menu on screen, so an editor
|
|
1662
|
-
there keeps drawing its own control. */}
|
|
1663
|
-
{/* Scoped to the row's OWN editor: the absorbed siblings above
|
|
1664
|
-
are other fields, which this row did not open. */}
|
|
1665
|
-
<RowOpenPickerContext.Provider value={openThePicker}>
|
|
1666
|
-
<RowMenuContext.Provider value={rowMenu}>
|
|
1667
|
-
{renderOption(optionName, optionField, 'small', true)}
|
|
1668
|
-
</RowMenuContext.Provider>
|
|
1669
|
-
</RowOpenPickerContext.Provider>
|
|
1670
|
-
</div>
|
|
1671
|
-
<StyledRowActions>
|
|
1672
|
-
{draftChip}
|
|
1673
|
-
{/* No Required tag here: while editing, the editor's own
|
|
1674
|
-
OptionFieldMessages strip below already says it. */}
|
|
1675
|
-
{editRowRevertButton}
|
|
1676
|
-
{cancelEditButton}
|
|
1677
|
-
{clearValueButton}
|
|
1678
|
-
{inlineOptionActions.map((action, index) =>
|
|
1679
|
-
renderInjectedOptionAction(action, index)
|
|
1680
|
-
)}
|
|
1681
|
-
{moreMenu}
|
|
1682
|
-
{closeExpandedFieldButton}
|
|
1683
|
-
</StyledRowActions>
|
|
1684
|
-
{/* Cluster node is rendered LAST (it's absolutely positioned, so DOM
|
|
1685
|
-
order doesn't move it) — keeping it out of the leading cells lets
|
|
1686
|
-
the `> div:nth-child(1/2/3)` editing-row alignment nudges land on
|
|
1687
|
-
label / editor / actions instead of being shifted by the node. */}
|
|
1688
|
-
{clusterNode}
|
|
1689
|
-
</div>
|
|
1690
|
-
);
|
|
1691
|
-
// ALWAYS wrap the editing row in the same StyledColumn so its parent stays
|
|
1692
|
-
// stable (the editor must not remount and lose focus mid-type). The
|
|
1693
|
-
// message panel is NOT rendered here: the editor's own OptionFieldMessages
|
|
1694
|
-
// strip already shows the field's messages, so a panel would duplicate
|
|
1695
|
-
// them. The wrapper carries the cluster rail class so the connection
|
|
1696
|
-
// persists while a member is being edited.
|
|
1697
|
-
return (
|
|
1698
|
-
<StyledColumn
|
|
1699
|
-
key={optionName}
|
|
1700
|
-
data-field={optionName}
|
|
1701
|
-
className={clusterBlockClass || undefined}
|
|
1702
|
-
>
|
|
1703
|
-
{editingRow}
|
|
1704
|
-
{/* Focused editing for an INLINE row. The edit card wraps its editor
|
|
1705
|
-
in <FocusedEditing>, which renders the field in place AND in the
|
|
1706
|
-
modal; an inline row already renders its editor in the row grid,
|
|
1707
|
-
so it mounts the modal alone. Without this the More menu's "Edit
|
|
1708
|
-
fullscreen" set the state and nothing appeared — the modal simply
|
|
1709
|
-
had no mount point on this branch. */}
|
|
1710
|
-
{focusedEditing === optionName ?
|
|
1711
|
-
<FocusedEditing
|
|
1712
|
-
isFullscreen
|
|
1713
|
-
modalOnly
|
|
1714
|
-
onClose={() => setFocusedEditing(undefined)}
|
|
1715
|
-
description={(schema?.display_name as string) || optionName}
|
|
1716
|
-
>
|
|
1717
|
-
<Description
|
|
1718
|
-
longDescription={schema?.desc}
|
|
1719
|
-
shortDescription={schema?.short_desc}
|
|
1720
|
-
longDescriptionShownByDefault
|
|
1721
|
-
/>
|
|
1722
|
-
{renderOption(optionName, optionField)}
|
|
1723
|
-
</FocusedEditing>
|
|
1724
|
-
: null}
|
|
1725
|
-
</StyledColumn>
|
|
1726
|
-
);
|
|
1727
|
-
}
|
|
1728
|
-
// Card chrome: badge / actions / tags render here — the row only fits
|
|
1729
|
-
// icon/image + the intent stripe.
|
|
1730
|
-
const schemaBadge = (schema as { badge?: unknown } | undefined)?.badge;
|
|
1731
|
-
const cardBadges =
|
|
1732
|
-
schemaBadge !== undefined && schemaBadge !== null ?
|
|
1733
|
-
((Array.isArray(schemaBadge) ? schemaBadge : [schemaBadge]) as unknown[])
|
|
1734
|
-
: [];
|
|
1735
|
-
const cardActions =
|
|
1736
|
-
(schema as { actions?: unknown[] } | undefined)?.actions?.filter(
|
|
1737
|
-
(action) => !!action && typeof action === 'object'
|
|
1738
|
-
) || [];
|
|
1739
|
-
const cardTags = ((schema as { tags?: unknown[] } | undefined)?.tags || []) as object[];
|
|
1740
|
-
const schemaIntentColor =
|
|
1741
|
-
schema?.intent ?
|
|
1742
|
-
(theme?.intents as Record<string, string> | undefined)?.[schema.intent as string]
|
|
1743
|
-
: undefined;
|
|
1744
|
-
return (
|
|
1745
|
-
<StyledEditCard
|
|
1746
|
-
key={optionName}
|
|
1747
|
-
ref={rowRef}
|
|
1748
|
-
data-field={optionName}
|
|
1749
|
-
className={
|
|
1750
|
-
COMPACT_SINGLE_VALUE_TYPES.has(editType) ?
|
|
1751
|
-
'options-readfirst-card options-readfirst-card-single'
|
|
1752
|
-
: 'options-readfirst-card'
|
|
1753
|
-
}
|
|
1754
|
-
$bg={cHover}
|
|
1755
|
-
$border={schemaIntentColor ? `${schemaIntentColor}66` : `${cInfo}66`}
|
|
1756
|
-
$calm={calm}
|
|
1757
|
-
>
|
|
1758
|
-
<div
|
|
1759
|
-
style={{
|
|
1760
|
-
display: 'flex',
|
|
1761
|
-
alignItems: 'flex-start',
|
|
1762
|
-
justifyContent: 'space-between',
|
|
1763
|
-
gap: 12,
|
|
1764
|
-
}}
|
|
1765
|
-
>
|
|
1766
|
-
<StyledCardHeading>
|
|
1767
|
-
<StyledCardLabel $color={cLabel}>
|
|
1768
|
-
{(
|
|
1769
|
-
(schema as { icon?: string } | undefined)?.icon ||
|
|
1770
|
-
(schema as { image?: string } | undefined)?.image
|
|
1771
|
-
) ?
|
|
1772
|
-
<ReqoreIcon
|
|
1773
|
-
icon={(schema as { icon?: string } | undefined)?.icon as never}
|
|
1774
|
-
image={(schema as { image?: string } | undefined)?.image}
|
|
1775
|
-
size='14px'
|
|
1776
|
-
/>
|
|
1777
|
-
: null}
|
|
1778
|
-
<span>
|
|
1779
|
-
{label}
|
|
1780
|
-
{required && !calm ?
|
|
1781
|
-
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1782
|
-
: null}
|
|
1783
|
-
{/* The `?` belongs here too. The read row and the inline
|
|
1784
|
-
editor both carry it; the edit CARD did not — and the card
|
|
1785
|
-
is the path every field with an `arg_schema` takes, so a
|
|
1786
|
-
nested form like an auth profile's Context Mapping opened
|
|
1787
|
-
with its long description unreachable. Expanding a field
|
|
1788
|
-
is exactly when its explanation is wanted, so losing the
|
|
1789
|
-
affordance on the way in is backwards. */}
|
|
1790
|
-
{helpIcon}
|
|
1791
|
-
</span>
|
|
1792
|
-
{cardBadges.map((badge, index) =>
|
|
1793
|
-
typeof badge === 'object' ?
|
|
1794
|
-
<ReqoreTag
|
|
1795
|
-
size='small'
|
|
1796
|
-
minimal
|
|
1797
|
-
key={index}
|
|
1798
|
-
className='options-readfirst-card-badge'
|
|
1799
|
-
{...(badge as object)}
|
|
1800
|
-
/>
|
|
1801
|
-
: <ReqoreTag
|
|
1802
|
-
size='small'
|
|
1803
|
-
minimal
|
|
1804
|
-
key={index}
|
|
1805
|
-
className='options-readfirst-card-badge'
|
|
1806
|
-
label={badge as string | number}
|
|
1807
|
-
/>
|
|
1808
|
-
)}
|
|
1809
|
-
</StyledCardLabel>
|
|
1810
|
-
{schema?.short_desc ?
|
|
1811
|
-
<ReqoreP size='small' effect={{ opacity: 0.6 }} style={{ marginTop: 2 }}>
|
|
1812
|
-
{schema.short_desc}
|
|
1813
|
-
</ReqoreP>
|
|
1814
|
-
: null}
|
|
1815
|
-
{cardTags.length ?
|
|
1816
|
-
<ReqoreTagGroup size='small' className='options-readfirst-card-tags'>
|
|
1817
|
-
{cardTags.map((tag, index) => (
|
|
1818
|
-
<ReqoreTag size='small' minimal key={index} {...(tag as object)} />
|
|
1819
|
-
))}
|
|
1820
|
-
</ReqoreTagGroup>
|
|
1821
|
-
: null}
|
|
1822
|
-
</StyledCardHeading>
|
|
1823
|
-
<ReqoreControlGroup fixed verticalAlign='center'>
|
|
1824
|
-
{cardActions.map((action, index) => {
|
|
1825
|
-
const { label: actionLabel, ...actionProps } = action as {
|
|
1826
|
-
label?: string;
|
|
1827
|
-
} & Record<string, unknown>;
|
|
1828
|
-
return (
|
|
1829
|
-
<ReqoreButton
|
|
1830
|
-
size='small'
|
|
1831
|
-
minimal
|
|
1832
|
-
flat
|
|
1833
|
-
fixed
|
|
1834
|
-
key={index}
|
|
1835
|
-
className='options-readfirst-card-action'
|
|
1836
|
-
{...(actionProps as object)}
|
|
1837
|
-
>
|
|
1838
|
-
{actionLabel}
|
|
1839
|
-
</ReqoreButton>
|
|
1840
|
-
);
|
|
1841
|
-
})}
|
|
1842
|
-
{inlineOptionActions.map((action, index) =>
|
|
1843
|
-
renderInjectedOptionAction(action, index)
|
|
1844
|
-
)}
|
|
1845
|
-
{/* Revert and Cancel, in the same order and with the same meaning
|
|
1846
|
-
as the inline row's strip.
|
|
1847
|
-
|
|
1848
|
-
They were absent here, and which shape a field opens into is
|
|
1849
|
-
decided by its TYPE — so an `any`-typed option (a test
|
|
1850
|
-
assertion's Expected Value, a Qog action state option) opened
|
|
1851
|
-
into this card and had no way out that discarded: Done
|
|
1852
|
-
committed, clicking away committed, and Escape did nothing at
|
|
1853
|
-
all because the handler is on the inline row's editor. The
|
|
1854
|
-
only exit from a card kept whatever had been typed. */}
|
|
1855
|
-
{editRowRevertButton}
|
|
1856
|
-
{cancelEditButton}
|
|
1857
|
-
{/* Clear-value sits before the More menu — the card analog of the
|
|
1858
|
-
inline row's Clear. Empties the value (keeps the field). */}
|
|
1859
|
-
{hasValue && !readOnly ?
|
|
1860
|
-
<ReqoreButton
|
|
1861
|
-
size='small'
|
|
1862
|
-
icon='DeleteBinLine'
|
|
1863
|
-
minimal
|
|
1864
|
-
flat
|
|
1865
|
-
fixed
|
|
1866
|
-
intent='danger'
|
|
1867
|
-
className='options-readfirst-clear'
|
|
1868
|
-
aria-label='Clear value'
|
|
1869
|
-
tooltip='Clear value'
|
|
1870
|
-
onClick={confirmClearValue}
|
|
1871
|
-
/>
|
|
1872
|
-
: null}
|
|
1873
|
-
{moreMenu}
|
|
1874
|
-
{closeExpandedFieldButton}
|
|
1875
|
-
</ReqoreControlGroup>
|
|
1876
|
-
</div>
|
|
1877
|
-
{/* Absorbed siblings render in the CARD form of an open field too,
|
|
1878
|
-
not only the inline row — which shape a field opens into depends
|
|
1879
|
-
on its type, and absorbing must not depend on that. */}
|
|
1880
|
-
{absorbedNodes}
|
|
1881
|
-
{/* Escape discards here for the same reason it does on the inline
|
|
1882
|
-
row: it is the one keystroke every editor treats as "get me out
|
|
1883
|
-
of this without saving", and a card that ignored it made the
|
|
1884
|
-
keystroke a silent commit. Same handler, same `cancelEdit()`, so
|
|
1885
|
-
the two shapes cannot diverge. */}
|
|
1886
|
-
<div
|
|
1887
|
-
// The card's editor, as the inline row's is: an opened card takes the
|
|
1888
|
-
// caret too (the tap-to-edit rule above), so opening Description or
|
|
1889
|
-
// Source Code by a click or by a blocked submit puts the cursor in it.
|
|
1890
|
-
ref={editorRef}
|
|
1891
|
-
className='options-readfirst-row-editor'
|
|
1892
|
-
style={{ display: 'contents' }}
|
|
1893
|
-
onKeyDown={(event) => {
|
|
1894
|
-
if (event.key === 'Escape') {
|
|
1895
|
-
event.stopPropagation();
|
|
1896
|
-
cancelEdit();
|
|
1897
|
-
}
|
|
1898
|
-
}}
|
|
1899
|
-
>
|
|
1900
|
-
{/* Same fullscreen focused-editing affordance as the classic cards —
|
|
1901
|
-
the modal mounts when this option is focused. */}
|
|
1902
|
-
<FocusedEditing
|
|
1903
|
-
isFullscreen={focusedEditing === optionName}
|
|
1904
|
-
onClose={() => setFocusedEditing(undefined)}
|
|
1905
|
-
description={(schema?.display_name as string) || optionName}
|
|
1906
|
-
>
|
|
1907
|
-
{focusedEditing === optionName ?
|
|
1908
|
-
<Description
|
|
1909
|
-
longDescription={schema?.desc}
|
|
1910
|
-
shortDescription={schema?.short_desc}
|
|
1911
|
-
longDescriptionShownByDefault
|
|
1912
|
-
/>
|
|
1913
|
-
: null}
|
|
1914
|
-
<RowMenuContext.Provider value={rowMenu}>
|
|
1915
|
-
{renderOption(optionName, optionField)}
|
|
1916
|
-
</RowMenuContext.Provider>
|
|
1917
|
-
</FocusedEditing>
|
|
1918
|
-
</div>
|
|
1919
|
-
</StyledEditCard>
|
|
1920
|
-
);
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
const formatted = formatOptionValue(optionField, schema);
|
|
1924
|
-
const empty = formatted === '';
|
|
1925
|
-
// A field with no value of its own but a declared default is not simply
|
|
1926
|
-
// unanswered: the default is what the server will use, and an em-dash says
|
|
1927
|
-
// the opposite. Render the default in its place, through the same formatter
|
|
1928
|
-
// so a list or an enum reads the way a real value would.
|
|
1929
|
-
//
|
|
1930
|
-
// This does NOT make the field count as answered. `empty` stays true, so the
|
|
1931
|
-
// row keeps the dimmed italic treatment that distinguishes a default from an
|
|
1932
|
-
// answer, and bucketing — which reads the field's own value, not this — still
|
|
1933
|
-
// files it under Optional and leaves it behind the collapse.
|
|
1934
|
-
//
|
|
1935
|
-
// `hidden` is included deliberately: it marks a field the form has not added
|
|
1936
|
-
// yet, which is the case that benefits most — the row is the author's first
|
|
1937
|
-
// sight of the field, and the default is what it will contribute if left
|
|
1938
|
-
// alone. Both flags only ever route to the em-dash below, so there is no
|
|
1939
|
-
// real value being displaced.
|
|
1940
|
-
const defaultValue = hidden || empty ? getDefaultValue(schema) : undefined;
|
|
1941
|
-
const formattedDefault =
|
|
1942
|
-
defaultValue === undefined || defaultValue === null ?
|
|
1943
|
-
''
|
|
1944
|
-
: formatOptionValue({ ...optionField, value: defaultValue }, schema);
|
|
1945
|
-
// Inline reasons shown on the value line: validation / dependency / one-of /
|
|
1946
|
-
// default-value hints, plus a read-only covered-by note (editable covered rows
|
|
1947
|
-
// carry that in their chip instead).
|
|
1948
|
-
const valueReasons: TInfoMsg[] = [
|
|
1949
|
-
...inlineMessages,
|
|
1950
|
-
...(empty && coveredByLabel ?
|
|
1951
|
-
[{ content: `Covered by “${coveredByLabel}”`, intent: calm ? undefined : 'success' }]
|
|
1952
|
-
: []),
|
|
1953
|
-
];
|
|
1954
|
-
// A hash row reveals its sub-fields as read-only sub-rows under a "view
|
|
1955
|
-
// more" disclosure; the row itself still expands the real editor on click.
|
|
1956
|
-
/* An EXPRESSION is not a hash, whatever shape it is stored in.
|
|
1957
|
-
|
|
1958
|
-
`{is_expression: true, value: {exp, args}}` is hash-shaped, so a field
|
|
1959
|
-
holding one earned the structured inset and the row printed the syntax
|
|
1960
|
-
tree — `is_expression true / value / exp + / args 1 2` — directly under a
|
|
1961
|
-
summary line that already read `1 + 2`. The AST is how an expression is
|
|
1962
|
-
STORED, not what it is; the row's line is the whole value, and the
|
|
1963
|
-
expression editor is where its parts are looked at. Same exclusion, and
|
|
1964
|
-
for the same reason, as `schema-definition` beside it. */
|
|
1965
|
-
const isExpression = getExpressionAst(optionField) !== undefined;
|
|
1966
|
-
const isPlainHashType =
|
|
1967
|
-
(valueType === 'hash' || valueType === 'free-hash') &&
|
|
1968
|
-
(schema as { ui_type?: string } | undefined)?.ui_type !== 'schema-definition' &&
|
|
1969
|
-
!isExpression;
|
|
1970
|
-
// A row of another declared type whose VALUE is an object (a data provider,
|
|
1971
|
-
// an options hash) gets the same preview: its summary line names the thing,
|
|
1972
|
-
// and the preview shows what it is made of. "11 fields" told the reader
|
|
1973
|
-
// neither, and read as a string that happened to say "fields". The
|
|
1974
|
-
// expression exclusion holds here too: a value stored as an AST is an
|
|
1975
|
-
// expression whatever its declared type.
|
|
1976
|
-
const isObjectValuedType =
|
|
1977
|
-
OBJECT_VALUED_PREVIEW_TYPES.has(valueType) &&
|
|
1978
|
-
!!optionField?.value &&
|
|
1979
|
-
typeof optionField.value === 'object' &&
|
|
1980
|
-
!Array.isArray(optionField.value) &&
|
|
1981
|
-
!isExpression;
|
|
1982
|
-
const hashEntries =
|
|
1983
|
-
!hidden && (isPlainHashType || isObjectValuedType) ?
|
|
1984
|
-
getHashEntries(optionField, schema)
|
|
1985
|
-
: [];
|
|
1986
|
-
// A LIST OF HASHES/objects gets the same expandable structured preview a hash
|
|
1987
|
-
// does — the flat "N items" summary can't convey object contents (and must
|
|
1988
|
-
// never print "[object Object]"). StructuredDataView unwraps each item's
|
|
1989
|
-
// {type,value} envelope. Plain scalar lists keep just their joined summary.
|
|
1990
|
-
const isHashList =
|
|
1991
|
-
!hidden &&
|
|
1992
|
-
(valueType === 'list' || valueType === 'free-list' || valueType === 'array') &&
|
|
1993
|
-
Array.isArray(optionField?.value) &&
|
|
1994
|
-
optionField.value.length > 0 &&
|
|
1995
|
-
optionField.value.some((item) => {
|
|
1996
|
-
if (richtextItemText(item) !== undefined) {
|
|
1997
|
-
return false;
|
|
1998
|
-
}
|
|
1999
|
-
const inner =
|
|
2000
|
-
item && typeof item === 'object' && 'value' in (item as Record<string, unknown>) ?
|
|
2001
|
-
(item as Record<string, unknown>).value
|
|
2002
|
-
: item;
|
|
2003
|
-
return inner !== null && typeof inner === 'object';
|
|
2004
|
-
});
|
|
2005
|
-
const showStructuredPreview = hashEntries.length > 0 || isHashList;
|
|
2006
|
-
// Multi-line code preview: a `code-editor` field with a non-empty string
|
|
2007
|
-
// value gets a `<pre>`-based code block under the row, capped by a
|
|
2008
|
-
// `ReqoreCollapsibleContent` so the collapsed height stays row-sized and
|
|
2009
|
-
// "Show more" reveals the rest. Sits in the same inset the hash/list
|
|
2010
|
-
// preview uses — mutually exclusive with `showStructuredPreview`.
|
|
2011
|
-
const showCodePreview =
|
|
2012
|
-
!hidden &&
|
|
2013
|
-
valueType === 'code-editor' &&
|
|
2014
|
-
typeof optionField?.value === 'string' &&
|
|
2015
|
-
optionField.value.length > 0;
|
|
2016
|
-
// A richtext value that renders as CHIPS is drawn in full by the row itself:
|
|
2017
|
-
// the chips are the value, and the reader is looking straight at it. The
|
|
2018
|
-
// native `title` below would then hover the raw source of the very thing the
|
|
2019
|
-
// chips replace ("[$fsminput:severity] $fsminput:alert_code") — and, because
|
|
2020
|
-
// each chip carries its own Reqore tooltip, hovering one fires BOTH: the chip
|
|
2021
|
-
// popover and the row's browser tooltip, in different places, saying almost
|
|
2022
|
-
// the same thing. Only the popover shows up in a screenshot, which is what
|
|
2023
|
-
// made the pair hard to report.
|
|
2024
|
-
const showsTemplateChips =
|
|
2025
|
-
!hidden &&
|
|
2026
|
-
valueType === 'richtext' &&
|
|
2027
|
-
Array.isArray(optionField?.value) &&
|
|
2028
|
-
richtextToSegments(optionField.value as never).some((segment) => segment.kind === 'tag');
|
|
2029
|
-
/* The same pair, for a value that IS one template rather than prose with
|
|
2030
|
-
templates embedded in it. `showsTemplateChips` above closed the richtext
|
|
2031
|
-
case and left this one open: a chosen reference renders as
|
|
2032
|
-
`ReadOnlyTemplateTag`, which carries its own popover, while the row went
|
|
2033
|
-
on setting a native `title` holding the raw reference. Hovering fired
|
|
2034
|
-
both — two tooltips in two places, and the one that says something useful
|
|
2035
|
-
is not the one a screenshot captures. */
|
|
2036
|
-
const showsTemplateTag =
|
|
2037
|
-
!hidden &&
|
|
2038
|
-
typeof optionField?.value === 'string' &&
|
|
2039
|
-
!(optionField as { is_expression?: boolean } | undefined)?.is_expression &&
|
|
2040
|
-
(isValueTemplate(optionField.value) ||
|
|
2041
|
-
!!findTemplate(
|
|
2042
|
-
(schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates ?? {},
|
|
2043
|
-
optionField.value
|
|
2044
|
-
));
|
|
2045
|
-
/* And once more for a value that only MENTIONS references — a predicate, a
|
|
2046
|
-
greeting with a name in it. The row draws those as prose plus chips too,
|
|
2047
|
-
so a native `title` would hover the raw spelling of exactly what the chips
|
|
2048
|
-
replaced, and each chip's own popover would fire alongside it. */
|
|
2049
|
-
const showsTemplateText =
|
|
2050
|
-
!hidden &&
|
|
2051
|
-
!showsTemplateTag &&
|
|
2052
|
-
!!formatted &&
|
|
2053
|
-
hasTemplateText(
|
|
2054
|
-
formatted,
|
|
2055
|
-
(schema as TFieldWithOwnTemplates | undefined)?.templates ?? templates,
|
|
2056
|
-
(schema as TFieldWithOwnTemplates | undefined)?.templateToken
|
|
2057
|
-
);
|
|
2058
|
-
// Markdown reads the same way for the same reason: the row itself can only
|
|
2059
|
-
// show a line of text, and for markdown that line is the SOURCE — the reader
|
|
2060
|
-
// gets `## ` and `**` where the point of the value is what it looks like
|
|
2061
|
-
// rendered. The document renders in the same inset the code preview uses,
|
|
2062
|
-
// through the host's renderer, so the row shows the description the way the
|
|
2063
|
-
// page it describes will.
|
|
2064
|
-
//
|
|
2065
|
-
// Only a MULTI-LINE document earns the inset. A one-liner is already fully
|
|
2066
|
-
// rendered as the row's own text, and a second copy of it below would make
|
|
2067
|
-
// every description row tall for nothing.
|
|
2068
|
-
const showMarkdownPreview =
|
|
2069
|
-
!hidden &&
|
|
2070
|
-
!showCodePreview &&
|
|
2071
|
-
!!markdownRenderer &&
|
|
2072
|
-
// keyed on the schema's ui_type, not on getValueType(): a markdown value
|
|
2073
|
-
// IS a string and the value type says so, which is the whole distinction
|
|
2074
|
-
// between how a value is stored and which editor draws it
|
|
2075
|
-
(schema as { ui_type?: string } | undefined)?.ui_type === 'markdown' &&
|
|
2076
|
-
typeof optionField?.value === 'string' &&
|
|
2077
|
-
isMultilineMarkdown(optionField.value);
|
|
2078
|
-
// Read-only: a long plain-text value is drawn in FULL under the row, in
|
|
2079
|
-
// the same inset and under the same "Show more" cap a code value gets,
|
|
2080
|
-
// instead of one line with an ellipsis and a browser tooltip holding the
|
|
2081
|
-
// rest. The editable row truncates because its value line is a summary
|
|
2082
|
-
// you click to open; a read row is the only rendering the value gets.
|
|
2083
|
-
//
|
|
2084
|
-
// "Plain" is `formatted === value`: the text was rendered verbatim — not
|
|
2085
|
-
// a template resolved to a chip, not an allowed value shown by its label,
|
|
2086
|
-
// not a sensitive value masked, not markdown summarised. Only text that
|
|
2087
|
-
// would not fit the line moves down: a break in it, or more than a short
|
|
2088
|
-
// sentence's worth of characters (a short value wraps in place).
|
|
2089
|
-
const showLongTextInset =
|
|
2090
|
-
readOnly &&
|
|
2091
|
-
!hidden &&
|
|
2092
|
-
!showCodePreview &&
|
|
2093
|
-
!showMarkdownPreview &&
|
|
2094
|
-
!showStructuredPreview &&
|
|
2095
|
-
typeof optionField?.value === 'string' &&
|
|
2096
|
-
formatted === optionField.value &&
|
|
2097
|
-
(optionField.value.includes('\n') || optionField.value.length > 120);
|
|
2098
|
-
// Read-only: a choice reads as the option AND what it means —
|
|
2099
|
-
// "Authenticated users", then "callers must present a token". The picker
|
|
2100
|
-
// showed that description while choosing; a reader has no picker.
|
|
2101
|
-
const chosenOption =
|
|
2102
|
-
readOnly && !hidden && !empty ?
|
|
2103
|
-
(findAllowedValueOption(optionField?.value, schema) as
|
|
2104
|
-
{ desc?: string; short_desc?: string } | undefined)
|
|
2105
|
-
: undefined;
|
|
2106
|
-
const readOnlyChoiceDesc = chosenOption?.desc || chosenOption?.short_desc;
|
|
2107
|
-
|
|
2108
|
-
// "9 lines" for a multi-line markdown value, shown under the field's name
|
|
2109
|
-
// rather than in front of its content: it describes the field, not the value,
|
|
2110
|
-
// and the value column is for the value. Rendered whether or not a host
|
|
2111
|
-
// renderer is present — the count is the one thing the row cannot show you by
|
|
2112
|
-
// showing you the text, and it is what tells you there is more below.
|
|
2113
|
-
const lineCountNote =
|
|
2114
|
-
(
|
|
2115
|
-
!hidden &&
|
|
2116
|
-
(schema as { ui_type?: string } | undefined)?.ui_type === 'markdown' &&
|
|
2117
|
-
typeof optionField?.value === 'string' &&
|
|
2118
|
-
isMultilineMarkdown(optionField.value)
|
|
2119
|
-
) ?
|
|
2120
|
-
`${optionField.value.trim().split('\n').length} lines`
|
|
2121
|
-
: undefined;
|
|
2122
|
-
const typeLabel =
|
|
2123
|
-
showFieldTypes ?
|
|
2124
|
-
`<${(schema?.ui_type as string) || (schema?.type as string) || 'auto'}${(schema as { ui_element_type?: string } | undefined)?.ui_element_type ? `[${(schema as { ui_element_type?: string }).ui_element_type}]` : ''}>`
|
|
2125
|
-
: null;
|
|
2126
|
-
// A `readonly` field is one the system owns — an assigned id, a recorded
|
|
2127
|
-
// provenance. The server states that on the field itself; only the older
|
|
2128
|
-
// compat projection also mapped it to `disabled`, so on this path the form
|
|
2129
|
-
// was handed `readonly` alone and rendered an editable control for a value
|
|
2130
|
-
// no answer of the user's can change.
|
|
2131
|
-
const fieldReadonly = !!(schema as { readonly?: boolean } | undefined)?.readonly;
|
|
2132
|
-
// Disabled rows (schema flag, readonly, or unmet deps) can't open — a lock +
|
|
2133
|
-
// reason renders instead. A form-level readOnly row never opens at all, and
|
|
2134
|
-
// shows no lock either: nothing on a read-only form is locked, it is read.
|
|
2135
|
-
const fieldDisabled =
|
|
2136
|
-
!hidden &&
|
|
2137
|
-
!readOnly &&
|
|
2138
|
-
(!!schema?.disabled ||
|
|
2139
|
-
fieldReadonly ||
|
|
2140
|
-
!hasAllDependenciesFullfilled(schema?.depends_on, availableOptions, options || {}));
|
|
2141
|
-
const fieldDisabledReason =
|
|
2142
|
-
schema?.disabled ? 'This field is disabled'
|
|
2143
|
-
: fieldReadonly ? 'Set by Qorus — not editable'
|
|
2144
|
-
: 'Disabled — dependencies are not fulfilled';
|
|
2145
|
-
// Dependency contract: top-level entries must ALL hold; a nested array
|
|
2146
|
-
// means ANY of its entries; `name=value` requires that exact value, and
|
|
2147
|
-
// `name!=value` requires any other answer. Parsed by the same helper the
|
|
2148
|
-
// predicate uses, so the lock cannot describe a rule the form does not apply.
|
|
2149
|
-
const dependencyEntries =
|
|
2150
|
-
fieldDisabled && !schema?.disabled && !fieldReadonly ?
|
|
2151
|
-
((schema?.depends_on || []) as (string | string[])[])
|
|
2152
|
-
: [];
|
|
2153
|
-
const describeDependency = (dep: string) => {
|
|
2154
|
-
const { name: depName, op, value: expected } = parseDependency(dep);
|
|
2155
|
-
const depLabel = (options?.[depName]?.display_name as string) || depName;
|
|
2156
|
-
const depValue = (availableOptions as TQorusForm)?.[depName]?.value;
|
|
2157
|
-
return {
|
|
2158
|
-
name: depName,
|
|
2159
|
-
exists: !!options?.[depName],
|
|
2160
|
-
// `!name` compares against nothing — it is satisfied by the sibling
|
|
2161
|
-
// being unanswered — so it says so, rather than going through the
|
|
2162
|
-
// comparison template and reading `Name ≠ undefined`.
|
|
2163
|
-
label:
|
|
2164
|
-
op === '!' ? `${depLabel} unset`
|
|
2165
|
-
: !op ? depLabel
|
|
2166
|
-
: `${depLabel} ${op === '=' ? '=' : '≠'} ${expected}`,
|
|
2167
|
-
fulfilled:
|
|
2168
|
-
op === '!' ? isOptionValueEmpty(depValue)
|
|
2169
|
-
: !op ? !isOptionValueEmpty(depValue)
|
|
2170
|
-
: depValue == null ? false
|
|
2171
|
-
: op === '=' ? String(depValue) === expected
|
|
2172
|
-
: String(depValue) !== expected,
|
|
2173
|
-
};
|
|
2174
|
-
};
|
|
2175
|
-
const depHighlightNames = (flatten(dependencyEntries as never[]) as string[])
|
|
2176
|
-
.map((dep) => describeDependency(dep).name)
|
|
2177
|
-
.filter((depName) => !!options?.[depName]);
|
|
2178
|
-
// Dependency lock, styled to match the required-group chip: a muted
|
|
2179
|
-
// "Depends on" dropdown whose items are the blocking fields (an "any of:"
|
|
2180
|
-
// divider for OR-groups), each clickable to flash/locate the blocker.
|
|
2181
|
-
const dependsOnChip =
|
|
2182
|
-
fieldDisabled && dependencyEntries.length ?
|
|
2183
|
-
<span
|
|
2184
|
-
style={{ display: 'inline-flex' }}
|
|
2185
|
-
role='presentation'
|
|
2186
|
-
onClick={(e) => e.stopPropagation()}
|
|
2187
|
-
onKeyDown={(e) => e.stopPropagation()}
|
|
2188
|
-
onMouseEnter={() => setHighlightedOptions(depHighlightNames)}
|
|
2189
|
-
onMouseLeave={() => setHighlightedOptions([])}
|
|
2190
|
-
>
|
|
2191
|
-
<ReqoreDropdown
|
|
2192
|
-
className='options-readfirst-lock-deps'
|
|
2193
|
-
size='tiny'
|
|
2194
|
-
minimal
|
|
2195
|
-
flat
|
|
2196
|
-
compact
|
|
2197
|
-
icon='LockLine'
|
|
2198
|
-
tooltip='Disabled — depends on other fields. Click to locate them.'
|
|
2199
|
-
items={[
|
|
2200
|
-
{ divider: true, label: 'Unlocked by:', dividerAlign: 'left' } as IReqoreDropdownItem,
|
|
2201
|
-
...dependencyEntries.flatMap((entry): IReqoreDropdownItem[] => [
|
|
2202
|
-
...(Array.isArray(entry) ?
|
|
2203
|
-
[{ divider: true, label: 'any of:', dividerAlign: 'left' } as IReqoreDropdownItem]
|
|
2204
|
-
: []),
|
|
2205
|
-
...(Array.isArray(entry) ? entry : [entry]).flatMap(
|
|
2206
|
-
(dep): IReqoreDropdownItem[] => {
|
|
2207
|
-
const info = describeDependency(dep);
|
|
2208
|
-
return info.exists ?
|
|
2209
|
-
[
|
|
2210
|
-
{
|
|
2211
|
-
label: info.label,
|
|
2212
|
-
icon: info.fulfilled ? 'CheckLine' : 'ArrowRightLine',
|
|
2213
|
-
intent: info.fulfilled ? 'success' : undefined,
|
|
2214
|
-
onClick: () => flashOption(info.name),
|
|
2215
|
-
} as IReqoreDropdownItem,
|
|
2216
|
-
]
|
|
2217
|
-
: [];
|
|
2218
|
-
}
|
|
2219
|
-
),
|
|
2220
|
-
]),
|
|
2221
|
-
]}
|
|
2222
|
-
/>
|
|
2223
|
-
</span>
|
|
2224
|
-
: null;
|
|
2225
|
-
const activate = (event?: { currentTarget?: Element | null }) => {
|
|
2226
|
-
// A read-only row is not a control. It used to open into the editor
|
|
2227
|
-
// with `readOnly` handed down (an id in a greyed-out input, a choice
|
|
2228
|
-
// that could still be changed); then into a read view of the same
|
|
2229
|
-
// value — which showed nothing the row could not show itself once it
|
|
2230
|
-
// stopped truncating. So it shows everything, and opens nothing HERE.
|
|
2231
|
-
//
|
|
2232
|
-
// Where the consumer says the field is editable somewhere else, the row
|
|
2233
|
-
// is the way in to that place: a reader clicking a value they want to
|
|
2234
|
-
// change has said exactly what they want, and answering with silence
|
|
2235
|
-
// teaches them the surface is broken.
|
|
2236
|
-
if (fieldDisabled || readOnly) {
|
|
2237
|
-
if (readOnly && !fieldDisabled) {
|
|
2238
|
-
onReadOnlyActivate?.(optionName);
|
|
2239
|
-
}
|
|
2240
|
-
return;
|
|
2241
|
-
}
|
|
2242
|
-
const target = event?.currentTarget as HTMLElement | undefined;
|
|
2243
|
-
if (target?.classList?.contains('readfirst-row')) {
|
|
2244
|
-
readRowHeights.current[optionName] = Math.round(target.getBoundingClientRect().height);
|
|
2245
|
-
}
|
|
2246
|
-
// The author asked for this one — so the row opens all the way (a picker
|
|
2247
|
-
// opens its list rather than reprinting the value as a closed trigger).
|
|
2248
|
-
// Only when it is OPENING: the same handler collapses an open row.
|
|
2249
|
-
openedByAuthor.current = !isExpanded;
|
|
2250
|
-
// Optional fields aren't "added" — every one is editable in place. Opening a
|
|
2251
|
-
// not-yet-set field just expands its editor (with an empty value); it joins
|
|
2252
|
-
// the form's output the moment a value is set (and moves to Set / Needs
|
|
2253
|
-
// attention), and drops back to Optional when cleared. No explicit add step.
|
|
2254
|
-
toggleExpandedOption(optionName);
|
|
2255
|
-
};
|
|
2256
|
-
|
|
2257
|
-
// Row chrome: icon/image before the label; schema `intent` as the edge
|
|
2258
|
-
// stripe (message-severity stripes win when both apply).
|
|
2259
|
-
const rowChromeIcon =
|
|
2260
|
-
(
|
|
2261
|
-
(schema as { icon?: string } | undefined)?.icon ||
|
|
2262
|
-
(schema as { image?: string } | undefined)?.image
|
|
2263
|
-
) ?
|
|
2264
|
-
<ReqoreIcon
|
|
2265
|
-
icon={(schema as { icon?: string } | undefined)?.icon as never}
|
|
2266
|
-
image={(schema as { image?: string } | undefined)?.image}
|
|
2267
|
-
size='14px'
|
|
2268
|
-
className='options-readfirst-row-icon'
|
|
2269
|
-
/>
|
|
2270
|
-
: null;
|
|
2271
|
-
const rowSchemaIntentColor =
|
|
2272
|
-
schema?.intent ?
|
|
2273
|
-
(theme?.intents as Record<string, string> | undefined)?.[schema.intent as string]
|
|
2274
|
-
: undefined;
|
|
2275
|
-
const rowStripeColor = (showStripe ? intentColor : undefined) || rowSchemaIntentColor;
|
|
2276
|
-
// Intent stripe on the block ROOT so it spans the field's whole territory:
|
|
2277
|
-
// the info/preview wrapper when one hangs below, else the bare row. It feeds
|
|
2278
|
-
// --readfirst-stripe, which StyledGroupBody paints as the value surface's left
|
|
2279
|
-
// border (the recessed surface starts at the value column).
|
|
2280
|
-
const stripeStyle =
|
|
2281
|
-
rowStripeColor ?
|
|
2282
|
-
({ ['--readfirst-stripe']: rowStripeColor } as React.CSSProperties)
|
|
2283
|
-
: undefined;
|
|
2284
|
-
|
|
2285
|
-
// "Focus" trailing status dot, derived from the SHARED status helper (the
|
|
2286
|
-
// same one FormEngine uses to bucket rows into the Needs-attention/Set/Optional
|
|
2287
|
-
// boxes — so the dot and the box can never disagree). Hidden (not-yet-added)
|
|
2288
|
-
// and dependency-locked rows show no dot (the add/lock affordance speaks for
|
|
2289
|
-
// them). worstIntent already folds in schema + validation messages.
|
|
2290
|
-
const cSuccess = (theme?.intents as Record<string, string> | undefined)?.success;
|
|
2291
|
-
const rowStatus = getReadFirstStatus({
|
|
2292
|
-
empty,
|
|
2293
|
-
required,
|
|
2294
|
-
covered: !!coveredByLabel || groupResolved,
|
|
2295
|
-
invalid: worstIntent === 'danger',
|
|
2296
|
-
warned: worstIntent === 'warning',
|
|
2297
|
-
visited,
|
|
2298
|
-
});
|
|
2299
|
-
// No dot at all in a read-only form: the dot reports progress on the
|
|
2300
|
-
// form, and a read view has no progress to report.
|
|
2301
|
-
//
|
|
2302
|
-
// Calm: no dot on a healthy row, and none on an empty required one either —
|
|
2303
|
-
// its value column already says "Required · add a value" in amber, and one
|
|
2304
|
-
// thing is said once. Only a failing value keeps the red dot beside the
|
|
2305
|
-
// reason it gives.
|
|
2306
|
-
const dotStatus =
|
|
2307
|
-
hidden || fieldDisabled || readOnly || (calm && rowStatus !== 'invalid') ?
|
|
2308
|
-
undefined
|
|
2309
|
-
: rowStatus;
|
|
2310
|
-
// Calm: an empty required value is said IN the value column, in amber,
|
|
2311
|
-
// where the value would be — instead of a dash plus a red reason beside it.
|
|
2312
|
-
// Not on a locked field (its lock and reason speak for it) or one that
|
|
2313
|
-
// shows the default it will fall back to.
|
|
2314
|
-
const calmRequiredWord =
|
|
2315
|
-
calm && empty && !hidden && !!schema?.required && !fieldDisabled && !formattedDefault ?
|
|
2316
|
-
readOnly ? CALM_REQUIRED_READ_LABEL
|
|
2317
|
-
: CALM_REQUIRED_LABEL
|
|
2318
|
-
: undefined;
|
|
2319
|
-
const dotColor =
|
|
2320
|
-
dotStatus === 'invalid' ? cDanger
|
|
2321
|
-
: dotStatus === 'todo' ? cWarning
|
|
2322
|
-
: dotStatus === 'set' ? cSuccess || cInfo
|
|
2323
|
-
: undefined;
|
|
2324
|
-
/* A read-only row is a control only when the consumer gave it somewhere to
|
|
2325
|
-
go. Then it takes a button's role, focus and keyboard handling, because
|
|
2326
|
-
a thing that acts on a click has to be reachable without a mouse and has
|
|
2327
|
-
to announce what it does — and "Edit X", not "X", is what it does. */
|
|
2328
|
-
const readOnlyIsAWayIn = !!readOnly && !fieldDisabled && !!onReadOnlyActivate;
|
|
2329
|
-
const actsOnClick = !readOnly || readOnlyIsAWayIn;
|
|
2330
|
-
|
|
2331
|
-
const row = (
|
|
2332
|
-
<div
|
|
2333
|
-
key={optionName}
|
|
2334
|
-
data-field={optionName}
|
|
2335
|
-
role={actsOnClick ? 'button' : undefined}
|
|
2336
|
-
tabIndex={actsOnClick ? 0 : undefined}
|
|
2337
|
-
aria-label={
|
|
2338
|
-
readOnlyIsAWayIn ? `Edit ${label}`
|
|
2339
|
-
: readOnly ? undefined
|
|
2340
|
-
: label
|
|
2341
|
-
}
|
|
2342
|
-
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 : ''}`}
|
|
2343
|
-
aria-disabled={fieldDisabled || undefined}
|
|
2344
|
-
style={readOnlyIsAWayIn ? { ...stripeStyle, cursor: 'pointer' } : stripeStyle}
|
|
2345
|
-
onClick={actsOnClick ? activate : undefined}
|
|
2346
|
-
onKeyDown={
|
|
2347
|
-
!actsOnClick ? undefined : (
|
|
2348
|
-
(event) => {
|
|
2349
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
2350
|
-
event.preventDefault();
|
|
2351
|
-
activate(event);
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
)
|
|
2355
|
-
}
|
|
2356
|
-
{...clusterHoverProps}
|
|
2357
|
-
>
|
|
2358
|
-
{clusterNode}
|
|
2359
|
-
<StyledLabelBlock className='options-readfirst-label-block'>
|
|
2360
|
-
<StyledRowLabel title={schema?.short_desc || undefined} $color={cLabel}>
|
|
2361
|
-
{rowChromeIcon}
|
|
2362
|
-
{/* label + asterisk + help flow as ONE inline run, so the asterisk
|
|
2363
|
-
stays right after the last word even when the name wraps. */}
|
|
2364
|
-
<span className='options-readfirst-label-text' style={{ minWidth: 0 }}>
|
|
2365
|
-
{label}
|
|
2366
|
-
{requiredMark ?
|
|
2367
|
-
<ReqoreIcon
|
|
2368
|
-
icon='Asterisk'
|
|
2369
|
-
color='danger'
|
|
2370
|
-
size='10px'
|
|
2371
|
-
margin='left'
|
|
2372
|
-
marginSize='tiny'
|
|
2373
|
-
/>
|
|
2374
|
-
: null}
|
|
2375
|
-
{helpIcon}
|
|
2376
|
-
</span>
|
|
2377
|
-
{typeLabel ?
|
|
2378
|
-
<ReqoreTag size='tiny' minimal label={typeLabel} labelEffect={{ opacity: 0.55 }} />
|
|
2379
|
-
: null}
|
|
2380
|
-
</StyledRowLabel>
|
|
2381
|
-
{showLabelDesc ?
|
|
2382
|
-
<StyledLabelDesc
|
|
2383
|
-
className='options-readfirst-label-desc'
|
|
2384
|
-
size='small'
|
|
2385
|
-
effect={{ opacity: 0.55 }}
|
|
2386
|
-
>
|
|
2387
|
-
{labelShortDesc}
|
|
2388
|
-
</StyledLabelDesc>
|
|
2389
|
-
: null}
|
|
2390
|
-
{codeSizeNode}
|
|
2391
|
-
{lineCountNote ?
|
|
2392
|
-
<ReqoreP
|
|
2393
|
-
className='options-readfirst-label-lines'
|
|
2394
|
-
size='tiny'
|
|
2395
|
-
effect={{ opacity: 0.45, textAlign: 'left' }}
|
|
2396
|
-
style={{ fontFamily: 'monospace' }}
|
|
2397
|
-
>
|
|
2398
|
-
{lineCountNote}
|
|
2399
|
-
</ReqoreP>
|
|
2400
|
-
: null}
|
|
2401
|
-
</StyledLabelBlock>
|
|
2402
|
-
<StyledRowValue
|
|
2403
|
-
// A code field draws its whole value in the preview below, with
|
|
2404
|
-
// "Show more" for the rest, so a hover carrying the same text is
|
|
2405
|
-
// noise -- and a native tooltip holding a few hundred lines is
|
|
2406
|
-
// unreadable anyway. A chipped richtext value is the same case: the row
|
|
2407
|
-
// already draws it in full, and its chips carry their own tooltips.
|
|
2408
|
-
// Every other value keeps its hover, which is the only way to read one
|
|
2409
|
-
// that the row had to truncate.
|
|
2410
|
-
title={
|
|
2411
|
-
(
|
|
2412
|
-
!empty &&
|
|
2413
|
-
!hidden &&
|
|
2414
|
-
// A read row wraps its value, so a hover has nothing to add.
|
|
2415
|
-
!readOnly &&
|
|
2416
|
-
!showCodePreview &&
|
|
2417
|
-
!showMarkdownPreview &&
|
|
2418
|
-
!showsTemplateChips &&
|
|
2419
|
-
!showsTemplateTag &&
|
|
2420
|
-
!showsTemplateText &&
|
|
2421
|
-
typeof formatted === 'string'
|
|
2422
|
-
) ?
|
|
2423
|
-
formatted
|
|
2424
|
-
: undefined
|
|
2425
|
-
}
|
|
2426
|
-
// A SET value reads at full key brightness so it stands out as the
|
|
2427
|
-
// actual data — crucial when stacked under a (muted) description on
|
|
2428
|
-
// mobile, where a dim value blends into the prose. The bold name still
|
|
2429
|
-
// outranks it; the empty dash stays faint.
|
|
2430
|
-
$color={
|
|
2431
|
-
calmRequiredWord ?
|
|
2432
|
-
rowStatus === 'pending' ?
|
|
2433
|
-
`${cMuted}a6`
|
|
2434
|
-
: cWarning
|
|
2435
|
-
: empty || hidden ? `${cMuted}66`
|
|
2436
|
-
: cKey
|
|
2437
|
-
}
|
|
2438
|
-
$empty={(empty || hidden) && !calmRequiredWord}
|
|
2439
|
-
>
|
|
2440
|
-
{/* A markdown value with a host renderer draws in full in the inset
|
|
2441
|
-
below, so the row prints no value line at all: the stripped-prose
|
|
2442
|
-
copy that used to sit above the document was a second rendering of
|
|
2443
|
-
the same text with the markers taken out (Qlip review, build #97).
|
|
2444
|
-
Without a renderer there is no inset, and the prose IS the value —
|
|
2445
|
-
so the line stays, and `renderReadFirstValue` still produces it.
|
|
2446
|
-
|
|
2447
|
-
A schema-previewed value is the same situation. The summary is a
|
|
2448
|
-
flattened join of the item labels ("Default RBAC, Cookie") sitting
|
|
2449
|
-
directly above a preview that names every one of those items and
|
|
2450
|
-
its fields — the same facts twice, the shorter version first, which
|
|
2451
|
-
reads less like a summary than like a second, disagreeing answer.
|
|
2452
|
-
An UNDESCRIBED value keeps its summary: the data tree beneath it
|
|
2453
|
-
says nothing about what the value MEANS, so the summary is still
|
|
2454
|
-
the only line that does.
|
|
2455
|
-
|
|
2456
|
-
Both suppressions are conditional on the replacement ACTUALLY
|
|
2457
|
-
rendering. `previewWithSchema` only asks whether the value and its
|
|
2458
|
-
`arg_schema` COULD be drawn as a schema preview; whether one is
|
|
2459
|
-
drawn is `showStructuredPreview`, and the two disagree for a field
|
|
2460
|
-
whose value type is a host ui_type rather than a list or a hash —
|
|
2461
|
-
a test's `cases` (`ui_type: "test-cases"`, an array of case hashes
|
|
2462
|
-
with an `arg_schema`) could be previewed but is not, because
|
|
2463
|
-
`isHashList` only recognises `list`/`free-list`/`array`. The row
|
|
2464
|
-
then dropped its summary in favour of a preview that never came
|
|
2465
|
-
and rendered nothing at all: a test with cases read as a bare
|
|
2466
|
-
"Cases" and looked unset until it was opened. */}
|
|
2467
|
-
{(
|
|
2468
|
-
showMarkdownPreview || showLongTextInset || (previewWithSchema && showStructuredPreview)
|
|
2469
|
-
) ?
|
|
2470
|
-
null
|
|
2471
|
-
: <span className='options-readfirst-valuetext'>
|
|
2472
|
-
{hidden || empty ?
|
|
2473
|
-
calmRequiredWord || formattedDefault || '—'
|
|
2474
|
-
: renderReadFirstValue(optionField, schema, formatted, readOnly)}
|
|
2475
|
-
</span>}
|
|
2476
|
-
{readOnlyChoiceDesc ?
|
|
2477
|
-
<span className='options-readfirst-choice-desc'>{readOnlyChoiceDesc}</span>
|
|
2478
|
-
: null}
|
|
2479
|
-
{(calmRequiredWord ?
|
|
2480
|
-
valueReasons.filter((m) => m.content !== calmRequiredWord)
|
|
2481
|
-
: valueReasons
|
|
2482
|
-
).map((m, i) => (
|
|
2483
|
-
<span
|
|
2484
|
-
key={`${m.content}-${i}`}
|
|
2485
|
-
className='options-readfirst-reason'
|
|
2486
|
-
style={{ color: reasonColor(m.intent) }}
|
|
2487
|
-
>
|
|
2488
|
-
{m.content}
|
|
2489
|
-
</span>
|
|
2490
|
-
))}
|
|
2491
|
-
{/* Dedicated schema message PANELS render right here, directly under the
|
|
2492
|
-
value (full width of the value column) — never pushed down by the
|
|
2493
|
-
label's short_desc. */}
|
|
2494
|
-
{panelMessages.length ?
|
|
2495
|
-
<ReqoreControlGroup
|
|
2496
|
-
className='options-readfirst-info-panel'
|
|
2497
|
-
vertical
|
|
2498
|
-
fluid
|
|
2499
|
-
gapSize='normal'
|
|
2500
|
-
>
|
|
2501
|
-
{panelMessages.map(renderInfoStrip)}
|
|
2502
|
-
</ReqoreControlGroup>
|
|
2503
|
-
: null}
|
|
2504
|
-
{/* The structured hash/list preview also lives in the value cell, so it
|
|
2505
|
-
starts directly under the value summary ("N fields"), not below the
|
|
2506
|
-
label's short_desc. */}
|
|
2507
|
-
{showStructuredPreview ?
|
|
2508
|
-
// The inset lives inside the row now, so a click on a value chip would
|
|
2509
|
-
// bubble to the row's onClick and fire activate() a SECOND time (the
|
|
2510
|
-
// chip's onItemClick already calls it) — toggling the editor shut again.
|
|
2511
|
-
// Stop the bubble here; the chip's own handler still opens the editor.
|
|
2512
|
-
<StyledRowInset
|
|
2513
|
-
className='options-readfirst-inset'
|
|
2514
|
-
onClick={(e) => e.stopPropagation()}
|
|
2515
|
-
>
|
|
2516
|
-
<ReqoreCollapsibleContent
|
|
2517
|
-
// The schema view spends more height per datum than a data tree
|
|
2518
|
-
// does — a labelled row rather than a packed chip — but there is
|
|
2519
|
-
// far less of it to read, so it gets enough room to show a
|
|
2520
|
-
// couple of complete items instead of fading out mid-row. A tree
|
|
2521
|
-
// of unknown depth keeps the tighter cap.
|
|
2522
|
-
maxCollapsedHeight={previewWithSchema ? 140 : 96}
|
|
2523
|
-
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
2524
|
-
>
|
|
2525
|
-
<div className='options-readfirst-structured'>
|
|
2526
|
-
{previewWithSchema ?
|
|
2527
|
-
// The form already knows this value's shape, names and
|
|
2528
|
-
// choices, so the preview says it rather than making the
|
|
2529
|
-
// reader decode it: "Scheme Type — Default RBAC", not a data
|
|
2530
|
-
// tree announcing "Object · 1 field" over `type: default`.
|
|
2531
|
-
<SchemaDataView
|
|
2532
|
-
value={optionField?.value}
|
|
2533
|
-
schema={previewArgSchema!}
|
|
2534
|
-
showTypes={showFieldTypes}
|
|
2535
|
-
colors={{
|
|
2536
|
-
key: cKey,
|
|
2537
|
-
muted: cMuted,
|
|
2538
|
-
// Deliberately stronger than `cDivider`. That tier is for
|
|
2539
|
-
// decorative hairlines between rows, where being nearly
|
|
2540
|
-
// invisible is the point. This rule is load-bearing — it
|
|
2541
|
-
// is the only thing saying which fields belong to which
|
|
2542
|
-
// numbered item — so it gets the muted tier at partial
|
|
2543
|
-
// alpha: quiet, but actually visible.
|
|
2544
|
-
border: `${cMuted}66`,
|
|
2545
|
-
// The identity accent for each item's heading.
|
|
2546
|
-
//
|
|
2547
|
-
// `custom1` first: BRAND_DESIGN §1 says apps may register
|
|
2548
|
-
// their own named intents and reference them like the
|
|
2549
|
-
// built-ins, and that is where a product's own accent
|
|
2550
|
-
// lives (qorus-ide registers its brand purple there).
|
|
2551
|
-
// Falling back to `info` keeps this working for a
|
|
2552
|
-
// consumer that registers nothing — the heading is
|
|
2553
|
-
// accented either way, just in the theme's own colour
|
|
2554
|
-
// rather than one this library picked.
|
|
2555
|
-
accent:
|
|
2556
|
-
(theme?.intents as Record<string, string> | undefined)?.custom1 || cInfo,
|
|
2557
|
-
}}
|
|
2558
|
-
onItemClick={() => activate()}
|
|
2559
|
-
/>
|
|
2560
|
-
: <StructuredDataView
|
|
2561
|
-
value={optionField?.value}
|
|
2562
|
-
collapsibleRoot={false}
|
|
2563
|
-
showTypes={showFieldTypes}
|
|
2564
|
-
defaultExpandDepth={2}
|
|
2565
|
-
onItemClick={() => activate()}
|
|
2566
|
-
/>
|
|
2567
|
-
}
|
|
2568
|
-
</div>
|
|
2569
|
-
</ReqoreCollapsibleContent>
|
|
2570
|
-
</StyledRowInset>
|
|
2571
|
-
: null}
|
|
2572
|
-
{/* Code-editor preview: same inset placement as hash/list — a monospace
|
|
2573
|
-
`<pre>` block wrapped in `ReqoreCollapsibleContent` so the collapsed
|
|
2574
|
-
row stays row-height while multi-line source reveals via "Show more".
|
|
2575
|
-
Stopping propagation here (like the structured inset) — a click on
|
|
2576
|
-
the collapse button must not also open the field editor. */}
|
|
2577
|
-
{showCodePreview ?
|
|
2578
|
-
<StyledRowInset
|
|
2579
|
-
className='options-readfirst-inset options-readfirst-code-inset'
|
|
2580
|
-
onClick={(e) => e.stopPropagation()}
|
|
2581
|
-
>
|
|
2582
|
-
<ReqoreCollapsibleContent
|
|
2583
|
-
maxCollapsedHeight={96}
|
|
2584
|
-
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
2585
|
-
>
|
|
2586
|
-
{codePreviewRenderer ?
|
|
2587
|
-
codePreviewRenderer({
|
|
2588
|
-
value: optionField.value as string,
|
|
2589
|
-
name: optionName,
|
|
2590
|
-
schema,
|
|
2591
|
-
options,
|
|
2592
|
-
values: availableOptions,
|
|
2593
|
-
})
|
|
2594
|
-
: <StyledCodePreview
|
|
2595
|
-
className='options-readfirst-code'
|
|
2596
|
-
$bg={cHover}
|
|
2597
|
-
$border={`${cDivider}88`}
|
|
2598
|
-
$fg={cKey}
|
|
2599
|
-
>
|
|
2600
|
-
{optionField.value as string}
|
|
2601
|
-
</StyledCodePreview>
|
|
2602
|
-
}
|
|
2603
|
-
</ReqoreCollapsibleContent>
|
|
2604
|
-
</StyledRowInset>
|
|
2605
|
-
: null}
|
|
2606
|
-
{/* Markdown preview: same inset, same collapse, drawn by the host's
|
|
2607
|
-
renderer so a description reads identically here and on the page it
|
|
2608
|
-
belongs to. Only rendered when a host supplied one — there is no
|
|
2609
|
-
built-in fallback, because drawing markdown in a dialect nobody
|
|
2610
|
-
chose is the problem this exists to avoid. Propagation stops here
|
|
2611
|
-
for the reason the code inset does it: a click on "Show more", or
|
|
2612
|
-
on a link in the prose, must not also open the field editor. */}
|
|
2613
|
-
{showMarkdownPreview ?
|
|
2614
|
-
<StyledRowInset
|
|
2615
|
-
className='options-readfirst-inset options-readfirst-markdown-inset'
|
|
2616
|
-
onClick={(e) => e.stopPropagation()}
|
|
2617
|
-
>
|
|
2618
|
-
<ReqoreCollapsibleContent
|
|
2619
|
-
maxCollapsedHeight={96}
|
|
2620
|
-
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
2621
|
-
>
|
|
2622
|
-
<div className='options-readfirst-markdown'>
|
|
2623
|
-
{markdownRenderer!({ value: optionField.value as string, compact: true })}
|
|
2624
|
-
</div>
|
|
2625
|
-
</ReqoreCollapsibleContent>
|
|
2626
|
-
</StyledRowInset>
|
|
2627
|
-
: null}
|
|
2628
|
-
{/* Read-only long text: see `showLongTextInset`. No click guard —
|
|
2629
|
-
the read row has no click to guard against. */}
|
|
2630
|
-
{showLongTextInset ?
|
|
2631
|
-
<StyledRowInset className='options-readfirst-inset options-readfirst-text-inset'>
|
|
2632
|
-
<ReqoreCollapsibleContent
|
|
2633
|
-
maxCollapsedHeight={96}
|
|
2634
|
-
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
2635
|
-
>
|
|
2636
|
-
<div className='options-readfirst-text'>{optionField.value as string}</div>
|
|
2637
|
-
</ReqoreCollapsibleContent>
|
|
2638
|
-
</StyledRowInset>
|
|
2639
|
-
: null}
|
|
2640
|
-
</StyledRowValue>
|
|
2641
|
-
<StyledRowActions>
|
|
2642
|
-
{/* Column discipline (table treatment): variable-width chips lead and
|
|
2643
|
-
rag INWARD; the lock/add and info slots are fixed-width and pinned at
|
|
2644
|
-
the right, so the ⓘ sits at the same x on every row. Revert is shown
|
|
2645
|
-
whenever a field has changed; delete reveals on hover. */}
|
|
2646
|
-
{/* No generic invalid/required chip: the intent stripe + the field's
|
|
2647
|
-
own message ("This field is required" for an empty required field)
|
|
2648
|
-
already flag it. Only the required-GROUP chip (One of / Covers)
|
|
2649
|
-
stays — it carries info nothing else does. */}
|
|
2650
|
-
{/* Railed (clustered) members: the rail conveys the grouping, so drop
|
|
2651
|
-
the "One of"/"Covers" chip — keep only "Covered by <X>", the one fact
|
|
2652
|
-
the rail can't show. Non-clustered members (split-across-panels or
|
|
2653
|
-
narrow mode, where there's no rail) keep the full chip as a fallback. */}
|
|
2654
|
-
{!hidden && !fieldDisabled && !clustered && !coveredByLabel ? requiredGroupChip : null}
|
|
2655
|
-
{!hidden ? dependsOnChip : null}
|
|
2656
|
-
{draftChip}
|
|
2657
|
-
{/* Remove-field lives in the expanded editor's "More" (⋮) menu now — no
|
|
2658
|
-
standalone delete button on the read row. */}
|
|
2659
|
-
{/* Lock slot BEFORE the info slot so the ⓘ keeps the same far-right x on
|
|
2660
|
-
every row — a disabled field's lock sits to the ⓘ's left rather than
|
|
2661
|
-
pushing it inward. (Not-yet-set optional fields show NO marker now —
|
|
2662
|
-
they're just editable in place, like every other row. Dependency-
|
|
2663
|
-
locked fields show the "Depends on" chip in the chips area instead.) */}
|
|
2664
|
-
{fieldDisabled && !dependencyEntries.length ?
|
|
2665
|
-
<StyledActionSlot className='options-readfirst-trailing-slot' $width={18}>
|
|
2666
|
-
<span title={fieldDisabledReason} style={{ display: 'inline-flex', opacity: 0.45 }}>
|
|
2667
|
-
<ReqoreIcon className='options-readfirst-locked' icon='LockLine' size='14px' />
|
|
2668
|
-
</span>
|
|
2669
|
-
</StyledActionSlot>
|
|
2670
|
-
: null}
|
|
2671
|
-
{infoToggle ?
|
|
2672
|
-
<StyledActionSlot className='options-readfirst-info-slot' $width={26}>
|
|
2673
|
-
{infoToggle}
|
|
2674
|
-
</StyledActionSlot>
|
|
2675
|
-
: null}
|
|
2676
|
-
{inlineOptionActions.map((action, index) =>
|
|
2677
|
-
renderInjectedOptionAction(action, index, 'tiny')
|
|
2678
|
-
)}
|
|
2679
|
-
{injectedOptionActionMenuItems.length ?
|
|
2680
|
-
<StyledActionSlot
|
|
2681
|
-
className='options-readfirst-actions-slot'
|
|
2682
|
-
$width={26}
|
|
2683
|
-
// The read row opens the editor when clicked. Without this the tap
|
|
2684
|
-
// that opens this menu also expands the row, which unmounts the
|
|
2685
|
-
// menu — on touch that made the collapsed actions unreachable,
|
|
2686
|
-
// the exact problem the collapse exists to solve.
|
|
2687
|
-
onClick={(event: React.MouseEvent<HTMLElement>) => event.stopPropagation()}
|
|
2688
|
-
>
|
|
2689
|
-
<ReqoreDropdown
|
|
2690
|
-
className='options-injected-actions-menu'
|
|
2691
|
-
icon='MoreFill'
|
|
2692
|
-
flat
|
|
2693
|
-
minimal
|
|
2694
|
-
fixed
|
|
2695
|
-
size='tiny'
|
|
2696
|
-
tooltip='Field actions'
|
|
2697
|
-
items={injectedOptionActionMenuItems}
|
|
2698
|
-
/>
|
|
2699
|
-
</StyledActionSlot>
|
|
2700
|
-
: null}
|
|
2701
|
-
{/* The revert affordance lives in the status-dot column (a changed field
|
|
2702
|
-
swaps its dot for the revert icon) so it sits at the same fixed x as
|
|
2703
|
-
the dot — out of the value's way instead of floating over it. */}
|
|
2704
|
-
<StyledActionSlot className='options-readfirst-statusdot-slot' $width={12}>
|
|
2705
|
-
{changed && !readOnly && !hidden ?
|
|
2706
|
-
<ReqoreButton
|
|
2707
|
-
className='options-readfirst-revert'
|
|
2708
|
-
size='tiny'
|
|
2709
|
-
flat
|
|
2710
|
-
minimal
|
|
2711
|
-
compact
|
|
2712
|
-
icon='HistoryLine'
|
|
2713
|
-
tooltip='Revert changes'
|
|
2714
|
-
onClick={(e: React.MouseEvent) => {
|
|
2715
|
-
e.stopPropagation();
|
|
2716
|
-
handleValueChange(
|
|
2717
|
-
optionName,
|
|
2718
|
-
originalValue.current?.[optionName]?.value,
|
|
2719
|
-
originalValue.current?.[optionName]?.type
|
|
2720
|
-
);
|
|
2721
|
-
}}
|
|
2722
|
-
/>
|
|
2723
|
-
: dotColor ?
|
|
2724
|
-
<StyledStatusDot
|
|
2725
|
-
className='options-readfirst-status-dot'
|
|
2726
|
-
$color={dotColor}
|
|
2727
|
-
$ring={dotStatus !== 'set'}
|
|
2728
|
-
aria-hidden
|
|
2729
|
-
/>
|
|
2730
|
-
: null}
|
|
2731
|
-
</StyledActionSlot>
|
|
2732
|
-
</StyledRowActions>
|
|
2733
|
-
</div>
|
|
2734
|
-
);
|
|
2735
|
-
|
|
2736
|
-
return row;
|
|
2737
|
-
}
|
|
2738
|
-
);
|
|
2739
|
-
|
|
2740
|
-
CompactRow.displayName = 'CompactRow';
|