@qoretechnologies/reqraft 0.10.25 → 0.10.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +52 -23
- package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
- package/dist/components/FocusedEditing/index.d.ts +9 -1
- package/dist/components/FocusedEditing/index.d.ts.map +1 -1
- package/dist/components/FocusedEditing/index.js +2 -2
- package/dist/components/FocusedEditing/index.js.map +1 -1
- package/dist/components/composer/Composer.d.ts +108 -0
- package/dist/components/composer/Composer.d.ts.map +1 -0
- package/dist/components/composer/Composer.js +238 -0
- package/dist/components/composer/Composer.js.map +1 -0
- package/dist/components/composer/helpers.d.ts +21 -0
- package/dist/components/composer/helpers.d.ts.map +1 -0
- package/dist/components/composer/helpers.js +75 -0
- package/dist/components/composer/helpers.js.map +1 -0
- package/dist/components/composer/index.d.ts +3 -0
- package/dist/components/composer/index.d.ts.map +1 -0
- package/dist/components/composer/index.js +19 -0
- package/dist/components/composer/index.js.map +1 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +307 -110
- 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 +123 -106
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +107 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +618 -179
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +9 -0
- 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 +7 -3
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +88 -51
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +28 -0
- package/dist/components/form/engine/optionActions.d.ts.map +1 -0
- package/dist/components/form/engine/optionActions.js +18 -0
- package/dist/components/form/engine/optionActions.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +63 -2
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +121 -6
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +35 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
- package/dist/components/form/engine/rendererTypes.js +63 -0
- package/dist/components/form/engine/rendererTypes.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.js +80 -0
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.js +138 -0
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.js +139 -0
- package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
- package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantModel.js +133 -0
- package/dist/components/form/engine/variants/variantModel.js.map +1 -0
- package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantParts.js +191 -0
- package/dist/components/form/engine/variants/variantParts.js.map +1 -0
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +13 -2
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +4 -1
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +5 -3
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +42 -8
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/binary/Binary.d.ts +15 -0
- package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
- package/dist/components/form/fields/binary/Binary.js +74 -0
- package/dist/components/form/fields/binary/Binary.js.map +1 -0
- package/dist/components/form/fields/boolean/Boolean.js +1 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +29 -12
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +52 -16
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +2 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
- package/dist/components/imageLightbox/ImageLightbox.js +169 -0
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
- package/dist/components/imageLightbox/index.d.ts +2 -0
- package/dist/components/imageLightbox/index.d.ts.map +1 -0
- package/dist/components/imageLightbox/index.js +18 -0
- package/dist/components/imageLightbox/index.js.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
- package/dist/components/searchFilterBar/index.d.ts +2 -0
- package/dist/components/searchFilterBar/index.d.ts.map +1 -0
- package/dist/components/searchFilterBar/index.js +18 -0
- package/dist/components/searchFilterBar/index.js.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.js +122 -0
- package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.js +295 -0
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
- package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketHeader.js +149 -0
- package/dist/components/supportTicket/TicketHeader.js.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.js +21 -0
- package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.js +115 -0
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.js +132 -0
- package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.js +28 -0
- package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
- package/dist/components/supportTicket/index.d.ts +11 -0
- package/dist/components/supportTicket/index.d.ts.map +1 -0
- package/dist/components/supportTicket/index.js +27 -0
- package/dist/components/supportTicket/index.js.map +1 -0
- package/dist/components/supportTicket/meta.d.ts +71 -0
- package/dist/components/supportTicket/meta.d.ts.map +1 -0
- package/dist/components/supportTicket/meta.js +115 -0
- package/dist/components/supportTicket/meta.js.map +1 -0
- package/dist/components/supportTicket/ticketModel.d.ts +102 -0
- package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
- package/dist/components/supportTicket/ticketModel.js +3 -0
- package/dist/components/supportTicket/ticketModel.js.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.js +492 -0
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
- package/dist/components/ticketReferences/index.d.ts +3 -0
- package/dist/components/ticketReferences/index.d.ts.map +1 -0
- package/dist/components/ticketReferences/index.js +6 -0
- package/dist/components/ticketReferences/index.js.map +1 -0
- package/dist/components/ticketThread/TicketThread.d.ts +57 -0
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
- package/dist/components/ticketThread/TicketThread.js +299 -0
- package/dist/components/ticketThread/TicketThread.js.map +1 -0
- package/dist/components/ticketThread/index.d.ts +3 -0
- package/dist/components/ticketThread/index.d.ts.map +1 -0
- package/dist/components/ticketThread/index.js +7 -0
- package/dist/components/ticketThread/index.js.map +1 -0
- package/dist/helpers/common.d.ts +5 -0
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +20 -1
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/scrollbar.d.ts +2 -0
- package/dist/helpers/scrollbar.d.ts.map +1 -0
- package/dist/helpers/scrollbar.js +25 -0
- package/dist/helpers/scrollbar.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/StorageProvider.d.ts +13 -0
- package/dist/providers/StorageProvider.d.ts.map +1 -1
- package/dist/providers/StorageProvider.js +29 -5
- package/dist/providers/StorageProvider.js.map +1 -1
- package/package.json +6 -5
- package/src/components/FocusedEditing/index.tsx +17 -2
- package/src/components/composer/Composer.stories.tsx +238 -0
- package/src/components/composer/Composer.tsx +545 -0
- package/src/components/composer/helpers.ts +74 -0
- package/src/components/composer/index.ts +2 -0
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
- package/src/components/form/engine/CompactRow.tsx +561 -283
- package/src/components/form/engine/CompactToolbar.tsx +216 -182
- package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
- package/src/components/form/engine/FormEngine.tsx +886 -170
- package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
- package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
- package/src/components/form/engine/compactRowContext.ts +14 -0
- package/src/components/form/engine/compactRowStyles.ts +273 -146
- package/src/components/form/engine/compactToolbarContext.ts +1 -0
- package/src/components/form/engine/optionActions.ts +40 -0
- package/src/components/form/engine/readFirst.ts +169 -10
- package/src/components/form/engine/rendererTypes.ts +55 -0
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
- package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
- package/src/components/form/engine/variants/VariantCards.tsx +212 -0
- package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
- package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
- package/src/components/form/engine/variants/focusDemo.ts +145 -0
- package/src/components/form/engine/variants/variantModel.ts +216 -0
- package/src/components/form/engine/variants/variantParts.tsx +313 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
- package/src/components/form/expressions/builder/index.tsx +12 -1
- package/src/components/form/expressions/useExpressions.ts +4 -1
- package/src/components/form/fields/Field.stories.tsx +40 -0
- package/src/components/form/fields/Field.tsx +10 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
- package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
- package/src/components/form/fields/binary/Binary.tsx +81 -0
- package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
- package/src/components/form/fields/boolean/Boolean.tsx +1 -1
- package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
- package/src/components/form/fields/color/Color.stories.tsx +30 -0
- package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
- package/src/components/form/fields/date/Date.stories.tsx +22 -0
- package/src/components/form/fields/file/File.stories.tsx +48 -0
- package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
- package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
- package/src/components/form/fields/number/Number.stories.tsx +31 -0
- package/src/components/form/fields/object/Object.stories.tsx +88 -0
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
- package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
- package/src/components/form/fields/select/Select.stories.tsx +108 -0
- package/src/components/form/fields/select/Select.tsx +38 -12
- package/src/components/form/fields/select/SelectCollection.tsx +78 -17
- package/src/components/form/fields/string/String.stories.tsx +16 -0
- package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
- package/src/components/form/fields/url/Url.stories.tsx +24 -0
- package/src/components/form/index.tsx +2 -0
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
- package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
- package/src/components/imageLightbox/index.ts +1 -0
- package/src/components/log/Log.stories.tsx +96 -0
- package/src/components/menu/Menu.stories.tsx +54 -0
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
- package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
- package/src/components/searchFilterBar/index.ts +1 -0
- package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
- package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
- package/src/components/supportTicket/AttachmentChips.tsx +147 -0
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
- package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
- package/src/components/supportTicket/ReferencePicker.tsx +563 -0
- package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
- package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
- package/src/components/supportTicket/TicketHeader.tsx +247 -0
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
- package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
- package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
- package/src/components/supportTicket/attachmentAccess.ts +97 -0
- package/src/components/supportTicket/fileToAttachment.ts +24 -0
- package/src/components/supportTicket/index.ts +10 -0
- package/src/components/supportTicket/meta.ts +185 -0
- package/src/components/supportTicket/ticketModel.ts +131 -0
- package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
- package/src/components/ticketReferences/TicketReferences.tsx +886 -0
- package/src/components/ticketReferences/index.ts +2 -0
- package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
- package/src/components/ticketThread/TicketThread.tsx +704 -0
- package/src/components/ticketThread/index.ts +6 -0
- package/src/helpers/common.ts +19 -0
- package/src/helpers/scrollbar.ts +32 -0
- package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
- package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
- package/src/index.tsx +8 -1
- package/src/providers/StorageProvider.tsx +30 -4
- package/src/stores/currentUser/currentUser.stories.tsx +49 -0
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ReqoreTagGroup,
|
|
11
11
|
} from '@qoretechnologies/reqore';
|
|
12
12
|
import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
13
|
+
import { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';
|
|
14
|
+
import { resolveOptionActions } from './optionActions';
|
|
13
15
|
import {
|
|
14
16
|
IQorusFormField,
|
|
15
17
|
TQorusForm,
|
|
@@ -35,17 +37,17 @@ import {
|
|
|
35
37
|
StyledActionSlot,
|
|
36
38
|
StyledCardHeading,
|
|
37
39
|
StyledCardLabel,
|
|
38
|
-
|
|
40
|
+
StyledCodePreview,
|
|
39
41
|
StyledColorSwatch,
|
|
40
42
|
StyledColumn,
|
|
41
43
|
StyledEditCard,
|
|
42
|
-
StyledInfoPanel,
|
|
43
44
|
StyledLabelBlock,
|
|
44
45
|
StyledLabelDesc,
|
|
45
46
|
StyledRowActions,
|
|
46
47
|
StyledRowInset,
|
|
47
48
|
StyledRowLabel,
|
|
48
49
|
StyledRowValue,
|
|
50
|
+
StyledStatusDot,
|
|
49
51
|
} from './compactRowStyles';
|
|
50
52
|
import { getOptionFieldMessages } from './OptionFieldMessages';
|
|
51
53
|
import {
|
|
@@ -55,7 +57,9 @@ import {
|
|
|
55
57
|
getAllowedValueImage,
|
|
56
58
|
getFileSize,
|
|
57
59
|
getHashEntries,
|
|
60
|
+
getReadFirstStatus,
|
|
58
61
|
getValueType,
|
|
62
|
+
isFixedCompactAllowedValueOption,
|
|
59
63
|
isOptionValueEmpty,
|
|
60
64
|
optionHasImages,
|
|
61
65
|
} from './readFirst';
|
|
@@ -92,7 +96,7 @@ const COMPACT_COMPLEX_TYPES = new Set([
|
|
|
92
96
|
|
|
93
97
|
// Card editors that hold a SINGLE value through one text-bearing input — their
|
|
94
98
|
// built-in ReqoreInput clear duplicates the card's own "Clear value" action, so
|
|
95
|
-
// we suppress it (the card
|
|
99
|
+
// we suppress it (the card trash action is the single source). Structural/multi-input
|
|
96
100
|
// editors (hash, list, schema…) and polymorphic ones (auto/any) are NOT listed:
|
|
97
101
|
// their per-sub-field clears are distinct affordances and must stay, while the
|
|
98
102
|
// card ✕ clears the whole value. (Operators force a card on a scalar value, so
|
|
@@ -106,6 +110,12 @@ const COMPACT_SINGLE_VALUE_TYPES = new Set([
|
|
|
106
110
|
'method-name',
|
|
107
111
|
]);
|
|
108
112
|
|
|
113
|
+
// How many consumer-injected actions may sit inline in a row before the rest
|
|
114
|
+
// overflow into the row's menu. A row's action slot shares space with the
|
|
115
|
+
// value; an unbounded button strip would squeeze the value out, and the
|
|
116
|
+
// consumer controls how many actions it injects.
|
|
117
|
+
const MAX_INLINE_OPTION_ACTIONS = 2;
|
|
118
|
+
|
|
109
119
|
|
|
110
120
|
// One read-first row: label | value | action collapsed; the real editor (the
|
|
111
121
|
// classic renderOption) expanded. `hidden` = search-surfaced optional —
|
|
@@ -142,34 +152,23 @@ export const CompactRow = memo(
|
|
|
142
152
|
const isExpanded = useContextSelector(CompactRowContext, (v) =>
|
|
143
153
|
v.expandedOptions.includes(optionName)
|
|
144
154
|
);
|
|
155
|
+
const autoFocusNameRef = useContextSelector(CompactRowContext, (v) => v.autoFocusNameRef);
|
|
145
156
|
const isHighlighted = useContextSelector(CompactRowContext, (v) =>
|
|
146
157
|
v.highlightedOptions.includes(optionName)
|
|
147
158
|
);
|
|
148
159
|
const isFlashed = useContextSelector(CompactRowContext, (v) =>
|
|
149
160
|
v.flashedOptions.includes(optionName)
|
|
150
161
|
);
|
|
151
|
-
const infoPanelOverride = useContextSelector(
|
|
152
|
-
CompactRowContext,
|
|
153
|
-
(v) => v.infoPanelOverrides[optionName]
|
|
154
|
-
);
|
|
155
162
|
const setHighlightedOptions = useContextSelector(
|
|
156
163
|
CompactRowContext,
|
|
157
164
|
(v) => v.setHighlightedOptions
|
|
158
165
|
);
|
|
159
|
-
const setInfoPanelOverrides = useContextSelector(
|
|
160
|
-
CompactRowContext,
|
|
161
|
-
(v) => v.setInfoPanelOverrides
|
|
162
|
-
);
|
|
163
166
|
const setFocusedEditing = useContextSelector(CompactRowContext, (v) => v.setFocusedEditing);
|
|
164
167
|
const readRowHeights = useContextSelector(CompactRowContext, (v) => v.readRowHeights);
|
|
165
168
|
const originalValue = useContextSelector(CompactRowContext, (v) => v.originalValue);
|
|
166
169
|
const availableOptions = useContextSelector(CompactRowContext, (v) => v.availableOptions);
|
|
167
170
|
const requiredGroupsInfo = useContextSelector(CompactRowContext, (v) => v.requiredGroupsInfo);
|
|
168
171
|
const handleValueChange = useContextSelector(CompactRowContext, (v) => v.handleValueChange);
|
|
169
|
-
const handleAddOptionalFieldChange = useContextSelector(
|
|
170
|
-
CompactRowContext,
|
|
171
|
-
(v) => v.handleAddOptionalFieldChange
|
|
172
|
-
);
|
|
173
172
|
const toggleExpandedOption = useContextSelector(
|
|
174
173
|
CompactRowContext,
|
|
175
174
|
(v) => v.toggleExpandedOption
|
|
@@ -186,18 +185,21 @@ export const CompactRow = memo(
|
|
|
186
185
|
);
|
|
187
186
|
const getTypeForOption = useContextSelector(CompactRowContext, (v) => v.getTypeForOption);
|
|
188
187
|
const confirmAction = useContextSelector(CompactRowContext, (v) => v.confirmAction);
|
|
188
|
+
const optionActions = useContextSelector(CompactRowContext, (v) => v.optionActions);
|
|
189
|
+
const collapseOptionActions = useContextSelector(
|
|
190
|
+
CompactRowContext,
|
|
191
|
+
(v) => v.collapseOptionActions
|
|
192
|
+
);
|
|
189
193
|
const renderOption = useContextSelector(CompactRowContext, (v) => v.renderOption);
|
|
190
194
|
const theme = useContextSelector(CompactRowContext, (v) => v.theme);
|
|
191
195
|
const cMuted = useContextSelector(CompactRowContext, (v) => v.cMuted);
|
|
192
196
|
const templates = useContextSelector(CompactRowContext, (v) => v.templates);
|
|
193
|
-
const cFaint = useContextSelector(CompactRowContext, (v) => v.cFaint);
|
|
194
197
|
const cKey = useContextSelector(CompactRowContext, (v) => v.cKey);
|
|
195
198
|
const cDivider = useContextSelector(CompactRowContext, (v) => v.cDivider);
|
|
196
199
|
const cHover = useContextSelector(CompactRowContext, (v) => v.cHover);
|
|
197
200
|
const cDanger = useContextSelector(CompactRowContext, (v) => v.cDanger);
|
|
198
201
|
const cWarning = useContextSelector(CompactRowContext, (v) => v.cWarning);
|
|
199
202
|
const cInfo = useContextSelector(CompactRowContext, (v) => v.cInfo);
|
|
200
|
-
const cBg = useContextSelector(CompactRowContext, (v) => v.cBg);
|
|
201
203
|
|
|
202
204
|
// Value-cell content: colour adds a swatch, file an icon + size; hash keeps
|
|
203
205
|
// its "N fields" summary (sub-fields reveal beneath the row).
|
|
@@ -270,6 +272,26 @@ export const CompactRow = memo(
|
|
|
270
272
|
);
|
|
271
273
|
}
|
|
272
274
|
|
|
275
|
+
// Code-editor read summary: replace the truncated string with a chip
|
|
276
|
+
// showing line count + character count. The actual code renders below
|
|
277
|
+
// the row as a monospace `<pre>` inside a `ReqoreCollapsibleContent` —
|
|
278
|
+
// see `showCodePreview` below. Keeps the row height fixed while making
|
|
279
|
+
// multi-line source readable without opening the full editor.
|
|
280
|
+
if (valueType === 'code-editor' && typeof field?.value === 'string') {
|
|
281
|
+
const lines = field.value.split('\n').length;
|
|
282
|
+
const chars = field.value.length;
|
|
283
|
+
return (
|
|
284
|
+
<ReqoreTag
|
|
285
|
+
size='small'
|
|
286
|
+
minimal
|
|
287
|
+
intent='info'
|
|
288
|
+
icon='CodeLine'
|
|
289
|
+
label={`${lines} ${lines === 1 ? 'line' : 'lines'}`}
|
|
290
|
+
labelKey={`${chars} ${chars === 1 ? 'char' : 'chars'}`}
|
|
291
|
+
/>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
273
295
|
// Selected allowed-value / enum item with a logo (e.g. language images):
|
|
274
296
|
// render it beside the value, where it belongs — not as the field's icon.
|
|
275
297
|
const allowedImage = getAllowedValueImage(field?.value, schema);
|
|
@@ -330,6 +352,97 @@ export const CompactRow = memo(
|
|
|
330
352
|
|
|
331
353
|
const schema = options?.[optionName];
|
|
332
354
|
const label = schema?.display_name || optionName;
|
|
355
|
+
const injectedOptionActions = React.useMemo<IReqorePanelAction[]>(
|
|
356
|
+
() =>
|
|
357
|
+
schema ?
|
|
358
|
+
resolveOptionActions(optionActions, {
|
|
359
|
+
name: optionName,
|
|
360
|
+
schema,
|
|
361
|
+
value: availableOptions?.[optionName],
|
|
362
|
+
}).filter((action) => action.show !== false)
|
|
363
|
+
: [],
|
|
364
|
+
[availableOptions?.[optionName], optionActions, optionName, schema]
|
|
365
|
+
);
|
|
366
|
+
// Which injected actions stay as inline buttons, and which move into the
|
|
367
|
+
// overflow menu. Everything collapses on touch / narrow viewports — a
|
|
368
|
+
// hover-gated button is unreachable without a hover — and anything past the
|
|
369
|
+
// inline cap overflows regardless, so a consumer injecting ten actions can
|
|
370
|
+
// never push the row's value out of view.
|
|
371
|
+
const [inlineOptionActions, menuOptionActions] = React.useMemo<
|
|
372
|
+
[IReqorePanelAction[], IReqorePanelAction[]]
|
|
373
|
+
>(
|
|
374
|
+
() =>
|
|
375
|
+
collapseOptionActions ?
|
|
376
|
+
[[], injectedOptionActions]
|
|
377
|
+
: [
|
|
378
|
+
injectedOptionActions.slice(0, MAX_INLINE_OPTION_ACTIONS),
|
|
379
|
+
injectedOptionActions.slice(MAX_INLINE_OPTION_ACTIONS),
|
|
380
|
+
],
|
|
381
|
+
[collapseOptionActions, injectedOptionActions]
|
|
382
|
+
);
|
|
383
|
+
const injectedOptionActionMenuItems = React.useMemo<IReqoreDropdownItem[]>(
|
|
384
|
+
() =>
|
|
385
|
+
menuOptionActions.map(
|
|
386
|
+
(action, index) =>
|
|
387
|
+
({
|
|
388
|
+
// A panel action labels itself with `label`, but an icon-only one
|
|
389
|
+
// (the IDE's AI-assist button) carries its name in the tooltip —
|
|
390
|
+
// a menu row has no hover affordance to fall back on.
|
|
391
|
+
label: action.label ?? action.tooltip ?? `Action ${index + 1}`,
|
|
392
|
+
icon: action.icon,
|
|
393
|
+
intent: action.intent,
|
|
394
|
+
disabled: action.disabled,
|
|
395
|
+
onClick: () => action.onClick?.(),
|
|
396
|
+
}) as IReqoreDropdownItem
|
|
397
|
+
),
|
|
398
|
+
[menuOptionActions]
|
|
399
|
+
);
|
|
400
|
+
const renderInjectedOptionAction = (
|
|
401
|
+
action: IReqorePanelAction,
|
|
402
|
+
index: number,
|
|
403
|
+
size: 'tiny' | 'small' = 'small'
|
|
404
|
+
) => {
|
|
405
|
+
// `show` drives visibility here instead of reaching the DOM. The classic
|
|
406
|
+
// path hands these to ReqorePanel, which honours `show: 'hover'` itself;
|
|
407
|
+
// the compact slots render plain buttons, so 'hover' becomes a CSS gate on
|
|
408
|
+
// the containing row/card (see compactRowStyles) — same config, same
|
|
409
|
+
// behaviour in both modes.
|
|
410
|
+
// `size` is destructured out and deliberately NOT honoured: the row owns
|
|
411
|
+
// its action strip's rhythm, and a consumer-supplied size made the
|
|
412
|
+
// injected button visibly smaller than the revert / more / confirm
|
|
413
|
+
// buttons beside it. The contextual `size` argument is the row's own
|
|
414
|
+
// (small when editing or in a card, tiny on a read row), so injected
|
|
415
|
+
// actions always match their neighbours.
|
|
416
|
+
const {
|
|
417
|
+
label: actionLabel,
|
|
418
|
+
onClick,
|
|
419
|
+
show,
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
421
|
+
size: _ignoredSize,
|
|
422
|
+
className,
|
|
423
|
+
...actionProps
|
|
424
|
+
} = action;
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<ReqoreButton
|
|
428
|
+
key={`${optionName}-injected-action-${index}`}
|
|
429
|
+
size={size}
|
|
430
|
+
minimal
|
|
431
|
+
flat
|
|
432
|
+
fixed
|
|
433
|
+
{...actionProps}
|
|
434
|
+
className={`${className ? `${className} ` : ''}options-injected-action${
|
|
435
|
+
show === 'hover' ? ' options-injected-action-hover' : ''
|
|
436
|
+
}`}
|
|
437
|
+
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
|
438
|
+
event.stopPropagation();
|
|
439
|
+
onClick?.();
|
|
440
|
+
}}
|
|
441
|
+
>
|
|
442
|
+
{actionLabel}
|
|
443
|
+
</ReqoreButton>
|
|
444
|
+
);
|
|
445
|
+
};
|
|
333
446
|
const required = !!(schema?.required || schema?.required_groups);
|
|
334
447
|
const removable =
|
|
335
448
|
!readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
|
|
@@ -408,7 +521,11 @@ export const CompactRow = memo(
|
|
|
408
521
|
/>
|
|
409
522
|
</span>
|
|
410
523
|
: null;
|
|
411
|
-
const
|
|
524
|
+
const fixedAllowedValueOption = isFixedCompactAllowedValueOption(schema);
|
|
525
|
+
const editType =
|
|
526
|
+
(fixedAllowedValueOption
|
|
527
|
+
? (schema?.type as string) || (schema?.ui_type as string)
|
|
528
|
+
: (schema?.ui_type as string) || (schema?.type as string)) ?? '';
|
|
412
529
|
// Scalars edit in place inside the row; complex fields (tall or nested
|
|
413
530
|
// editors) still open the expanded card below.
|
|
414
531
|
const inlineEditable =
|
|
@@ -420,6 +537,24 @@ export const CompactRow = memo(
|
|
|
420
537
|
// A choice with per-option logos (e.g. language) reads better collapsed.
|
|
421
538
|
!optionHasImages(schema) &&
|
|
422
539
|
!COMPACT_COMPLEX_TYPES.has(editType);
|
|
540
|
+
|
|
541
|
+
// Auto-focus the editor's first input when a field is opened, so you can type
|
|
542
|
+
// straight away (matches the prototype's tap-to-edit feel).
|
|
543
|
+
const editorRef = React.useRef<HTMLDivElement>(null);
|
|
544
|
+
React.useEffect(() => {
|
|
545
|
+
if (!isExpanded) return undefined;
|
|
546
|
+
const id = window.setTimeout(() => {
|
|
547
|
+
const el = editorRef.current?.querySelector<HTMLElement>(
|
|
548
|
+
'input:not([type="hidden"]):not([disabled]), textarea, [contenteditable="true"]'
|
|
549
|
+
);
|
|
550
|
+
// A user-driven expand scrolls the just-opened editor into view; the
|
|
551
|
+
// programmatic `autoFocusFirstRequired` expand must NOT — otherwise an
|
|
552
|
+
// off-screen / below-the-fold form scrolls itself into view on mount.
|
|
553
|
+
el?.focus(autoFocusNameRef?.current === optionName ? { preventScroll: true } : undefined);
|
|
554
|
+
}, 60);
|
|
555
|
+
return () => window.clearTimeout(id);
|
|
556
|
+
}, [isExpanded, optionName, autoFocusNameRef]);
|
|
557
|
+
|
|
423
558
|
const revertButton =
|
|
424
559
|
changed ?
|
|
425
560
|
<ReqoreButton
|
|
@@ -443,8 +578,9 @@ export const CompactRow = memo(
|
|
|
443
578
|
// — distinct from the read-row "Remove field" (which deletes an optional
|
|
444
579
|
// field entirely). Once cleared, `changed` flips on and `hasValue` off, so
|
|
445
580
|
// this button gives way to `revertButton` in the same slot — the morph the
|
|
446
|
-
// edit row's actions are designed around.
|
|
447
|
-
//
|
|
581
|
+
// edit row's actions are designed around. Clearing is destructive even
|
|
582
|
+
// though it can be reverted before saving, so both the trigger and its
|
|
583
|
+
// confirmation use danger intent.
|
|
448
584
|
//
|
|
449
585
|
// Only editors with NO built-in clear of their own get this button —
|
|
450
586
|
// toggles and fixed-choice pickers. The text/number/date inputs already
|
|
@@ -457,6 +593,17 @@ export const CompactRow = memo(
|
|
|
457
593
|
// CREATABLE one still renders the raw input (with its own ✕), so it keeps
|
|
458
594
|
// its clear — matching the dispatch in AutoFormField/TemplateField.
|
|
459
595
|
(!!size(schema?.allowed_values) && !schema?.allowed_values_creatable);
|
|
596
|
+
const confirmClearValue = (event?: React.MouseEvent) => {
|
|
597
|
+
event?.stopPropagation();
|
|
598
|
+
confirmAction({
|
|
599
|
+
title: 'Clear value',
|
|
600
|
+
description: `Clear the current value of "${label}"?`,
|
|
601
|
+
confirmLabel: 'Clear value',
|
|
602
|
+
confirmButtonIntent: 'danger',
|
|
603
|
+
intent: 'danger',
|
|
604
|
+
onConfirm: () => handleValueChange(optionName, undefined),
|
|
605
|
+
});
|
|
606
|
+
};
|
|
460
607
|
const clearValueButton =
|
|
461
608
|
hasValue && !readOnly && editorLacksOwnClear ?
|
|
462
609
|
<ReqoreButton
|
|
@@ -464,12 +611,11 @@ export const CompactRow = memo(
|
|
|
464
611
|
size='small'
|
|
465
612
|
flat
|
|
466
613
|
minimal
|
|
467
|
-
icon='
|
|
614
|
+
icon='DeleteBinLine'
|
|
615
|
+
intent='danger'
|
|
616
|
+
aria-label='Clear value'
|
|
468
617
|
tooltip='Clear value'
|
|
469
|
-
onClick={
|
|
470
|
-
e.stopPropagation();
|
|
471
|
-
handleValueChange(optionName, undefined);
|
|
472
|
-
}}
|
|
618
|
+
onClick={confirmClearValue}
|
|
473
619
|
/>
|
|
474
620
|
: null;
|
|
475
621
|
// Batched commit: a changed row is a draft until Save — mark it with the
|
|
@@ -489,6 +635,21 @@ export const CompactRow = memo(
|
|
|
489
635
|
effect={{ uppercase: true, spaced: 1 }}
|
|
490
636
|
/>
|
|
491
637
|
: null;
|
|
638
|
+
const isPassiveCloseAction = fixedAllowedValueOption || readOnly;
|
|
639
|
+
const closeExpandedFieldButton = (
|
|
640
|
+
<ReqoreButton
|
|
641
|
+
className='options-readfirst-done'
|
|
642
|
+
size='small'
|
|
643
|
+
intent={!isPassiveCloseAction ? 'success' : undefined}
|
|
644
|
+
flat={isPassiveCloseAction}
|
|
645
|
+
minimal={isPassiveCloseAction}
|
|
646
|
+
fixed
|
|
647
|
+
icon={isPassiveCloseAction ? 'CloseLine' : 'CheckLine'}
|
|
648
|
+
aria-label={isPassiveCloseAction ? 'Close field' : 'Done'}
|
|
649
|
+
tooltip={isPassiveCloseAction ? 'Close field' : 'Done'}
|
|
650
|
+
onClick={() => toggleExpandedOption(optionName)}
|
|
651
|
+
/>
|
|
652
|
+
);
|
|
492
653
|
|
|
493
654
|
// Info tiers: Tier 1 (danger/warning + dependency hints) must be visible
|
|
494
655
|
// without interaction; Tier 2 (info/success, default notes) sits behind ⓘ.
|
|
@@ -516,10 +677,13 @@ export const CompactRow = memo(
|
|
|
516
677
|
.filter((m) => m.label !== 'This field is required')
|
|
517
678
|
.map((m) => ({ intent: m.intent as string, content: String(m.label) }))
|
|
518
679
|
: [];
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
680
|
+
// TWO display channels, matching the Focus prototype:
|
|
681
|
+
// • dedicated schema `messages` → prominent coloured PANELS below the row;
|
|
682
|
+
// • everything else (validation reasons, the unmet-dependency hint, the
|
|
683
|
+
// default-value note) → a compact INLINE reason strip — not a panel.
|
|
684
|
+
const panelMessages = schemaMessages;
|
|
685
|
+
const inlineMessages: TInfoMsg[] = [
|
|
686
|
+
...fieldMessages,
|
|
523
687
|
...(infoActive && schema?.default_value_desc ?
|
|
524
688
|
[
|
|
525
689
|
{
|
|
@@ -529,26 +693,44 @@ export const CompactRow = memo(
|
|
|
529
693
|
]
|
|
530
694
|
: []),
|
|
531
695
|
];
|
|
696
|
+
const allMsgs = [...panelMessages, ...inlineMessages];
|
|
532
697
|
const worstIntent =
|
|
533
|
-
|
|
534
|
-
:
|
|
698
|
+
allMsgs.some((m) => m.intent === 'danger') ? 'danger'
|
|
699
|
+
: allMsgs.some((m) => m.intent === 'warning') ? 'warning'
|
|
535
700
|
: undefined;
|
|
536
701
|
const intentColor =
|
|
537
702
|
worstIntent === 'danger' ? cDanger
|
|
538
703
|
: worstIntent === 'warning' ? cWarning
|
|
539
704
|
: undefined;
|
|
540
705
|
const showStripe = infoActive && !!intentColor;
|
|
541
|
-
// The short_desc renders UNDER the field name (revealed by the ⓘ toggle); the
|
|
542
|
-
// value-side panel carries only the messages (tier1/tier2).
|
|
543
706
|
const labelShortDesc = schema?.short_desc;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
707
|
+
// The per-row ⓘ is gone: short_desc shows under the name only when the global
|
|
708
|
+
// "descriptions" toggle is engaged (and inside the editor when expanded).
|
|
709
|
+
const showLabelDesc = !!labelShortDesc && showAllDescriptions === true;
|
|
710
|
+
|
|
711
|
+
// The `?` that opens the long-form help dialog. Shared by the read row AND
|
|
712
|
+
// the inline editor: opening a field used to drop it, leaving an editing row
|
|
713
|
+
// with no route to the field's description at all. Stops propagation because
|
|
714
|
+
// both labels are click targets themselves (the row expands / collapses).
|
|
715
|
+
const helpIcon =
|
|
716
|
+
schema?.desc ?
|
|
717
|
+
<ReqoreIcon
|
|
718
|
+
icon='QuestionLine'
|
|
719
|
+
size='12px'
|
|
720
|
+
effect={{ opacity: 0.55 }}
|
|
721
|
+
margin='left'
|
|
722
|
+
marginSize='tiny'
|
|
723
|
+
role='button'
|
|
724
|
+
tabIndex={-1}
|
|
725
|
+
aria-label='Help'
|
|
726
|
+
className='options-readfirst-help'
|
|
727
|
+
style={{ cursor: 'help' }}
|
|
728
|
+
onClick={(event) => {
|
|
729
|
+
event.stopPropagation();
|
|
730
|
+
handleOptionLabelClick(optionName);
|
|
731
|
+
}}
|
|
732
|
+
/>
|
|
733
|
+
: null;
|
|
552
734
|
|
|
553
735
|
const renderInfoStrip = (m: TInfoMsg, index: number) => (
|
|
554
736
|
<ReqoreMessage
|
|
@@ -562,33 +744,14 @@ export const CompactRow = memo(
|
|
|
562
744
|
{m.content}
|
|
563
745
|
</ReqoreMessage>
|
|
564
746
|
);
|
|
747
|
+
const reasonColor = (intent?: string) =>
|
|
748
|
+
intent === 'danger' ? cDanger
|
|
749
|
+
: intent === 'warning' ? cWarning
|
|
750
|
+
: intent === 'success' ?
|
|
751
|
+
(theme?.intents as Record<string, string> | undefined)?.success || cInfo
|
|
752
|
+
: `${cMuted}cc`;
|
|
565
753
|
|
|
566
|
-
const infoToggle =
|
|
567
|
-
hasInfoPanelContent ?
|
|
568
|
-
<ReqoreButton
|
|
569
|
-
className='options-readfirst-info-toggle'
|
|
570
|
-
size='tiny'
|
|
571
|
-
minimal
|
|
572
|
-
flat
|
|
573
|
-
compact
|
|
574
|
-
fixed
|
|
575
|
-
active={infoPanelOpen}
|
|
576
|
-
intent={worstIntent as never}
|
|
577
|
-
icon={infoPanelOpen ? 'InformationFill' : 'InformationLine'}
|
|
578
|
-
tooltip={infoPanelOpen ? 'Hide field information' : 'Show field information'}
|
|
579
|
-
onClick={(e: React.MouseEvent) => {
|
|
580
|
-
e.stopPropagation();
|
|
581
|
-
setInfoPanelOverrides((prev) => ({ ...prev, [optionName]: !infoPanelOpen }));
|
|
582
|
-
}}
|
|
583
|
-
/>
|
|
584
|
-
: null;
|
|
585
|
-
|
|
586
|
-
const infoBlock =
|
|
587
|
-
infoPanelOpen && panelHasContent ?
|
|
588
|
-
<StyledInfoPanel className='options-readfirst-info-panel'>
|
|
589
|
-
{[...tier1, ...tier2].map(renderInfoStrip)}
|
|
590
|
-
</StyledInfoPanel>
|
|
591
|
-
: null;
|
|
754
|
+
const infoToggle = null;
|
|
592
755
|
|
|
593
756
|
// Cluster (required-group connection) — shared by the read row, its block
|
|
594
757
|
// wrapper AND the inline editor, so the rail/node/highlight persist across
|
|
@@ -604,20 +767,9 @@ export const CompactRow = memo(
|
|
|
604
767
|
clustered ?
|
|
605
768
|
`readfirst-cluster-rail${clusterFirst ? ' readfirst-cluster-first' : ''}${clusterLast ? ' readfirst-cluster-last' : ''}${clusterSatisfied ? ' readfirst-cluster-satisfied' : ''}`
|
|
606
769
|
: '';
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
<StyledClusterNode
|
|
611
|
-
className='options-readfirst-node'
|
|
612
|
-
$filled={!!memberSet}
|
|
613
|
-
$color={
|
|
614
|
-
clusterSatisfied ?
|
|
615
|
-
(theme?.intents as Record<string, string> | undefined)?.success || cInfo
|
|
616
|
-
: `${cWarning}99`
|
|
617
|
-
}
|
|
618
|
-
$bg={cBg}
|
|
619
|
-
/>
|
|
620
|
-
: null;
|
|
770
|
+
// The connection rail + status nodes are gone — the "One of the below is
|
|
771
|
+
// required" box (and the Covers / Covered-by chips) carry the grouping now.
|
|
772
|
+
const clusterNode = null;
|
|
621
773
|
const clusterHoverProps =
|
|
622
774
|
clustered && clusterMembers.length ?
|
|
623
775
|
{
|
|
@@ -626,6 +778,46 @@ export const CompactRow = memo(
|
|
|
626
778
|
}
|
|
627
779
|
: {};
|
|
628
780
|
|
|
781
|
+
// Secondary edit actions tuck into a "More" (⋮) menu so the card header stays
|
|
782
|
+
// calm: Fullscreen always, plus Remove field for a removable option. Rendered
|
|
783
|
+
// just before the Done ✓ in both expanded layouts.
|
|
784
|
+
const moreMenu = (
|
|
785
|
+
<ReqoreDropdown
|
|
786
|
+
className='options-readfirst-more'
|
|
787
|
+
icon='More2Fill'
|
|
788
|
+
flat
|
|
789
|
+
minimal
|
|
790
|
+
fixed
|
|
791
|
+
size='small'
|
|
792
|
+
tooltip='More actions'
|
|
793
|
+
items={[
|
|
794
|
+
{
|
|
795
|
+
label: 'Edit fullscreen',
|
|
796
|
+
icon: 'FullscreenLine',
|
|
797
|
+
onClick: () => setFocusedEditing(optionName),
|
|
798
|
+
} as IReqoreDropdownItem,
|
|
799
|
+
...(removable && !hidden ?
|
|
800
|
+
[
|
|
801
|
+
{
|
|
802
|
+
label: 'Remove field',
|
|
803
|
+
icon: 'DeleteBinLine',
|
|
804
|
+
intent: 'danger',
|
|
805
|
+
onClick: () =>
|
|
806
|
+
confirmAction({
|
|
807
|
+
title: 'Remove field',
|
|
808
|
+
onConfirm: () => removeSelectedOption(optionName),
|
|
809
|
+
}),
|
|
810
|
+
} as IReqoreDropdownItem,
|
|
811
|
+
]
|
|
812
|
+
: []),
|
|
813
|
+
// Consumer-injected actions that did not fit inline (or collapsed
|
|
814
|
+
// wholesale on touch) land here — this row already has a menu, so they
|
|
815
|
+
// reuse it rather than adding a second one beside it.
|
|
816
|
+
...injectedOptionActionMenuItems,
|
|
817
|
+
]}
|
|
818
|
+
/>
|
|
819
|
+
);
|
|
820
|
+
|
|
629
821
|
if (isExpanded) {
|
|
630
822
|
if (inlineEditable) {
|
|
631
823
|
const collapse = () => toggleExpandedOption(optionName);
|
|
@@ -641,25 +833,42 @@ export const CompactRow = memo(
|
|
|
641
833
|
}
|
|
642
834
|
{...clusterHoverProps}
|
|
643
835
|
>
|
|
644
|
-
<
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
event.
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
836
|
+
<StyledLabelBlock>
|
|
837
|
+
<StyledRowLabel
|
|
838
|
+
role='button'
|
|
839
|
+
tabIndex={0}
|
|
840
|
+
aria-label={`Collapse ${label}`}
|
|
841
|
+
title={schema?.short_desc || undefined}
|
|
842
|
+
$color={cKey}
|
|
843
|
+
$pointer
|
|
844
|
+
onClick={collapse}
|
|
845
|
+
onKeyDown={(event) => {
|
|
846
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
847
|
+
event.preventDefault();
|
|
848
|
+
collapse();
|
|
849
|
+
}
|
|
850
|
+
}}
|
|
851
|
+
>
|
|
852
|
+
{label}
|
|
853
|
+
{required ? <ReqoreIcon icon='Asterisk' color='danger' size='10px' /> : null}
|
|
854
|
+
{helpIcon}
|
|
855
|
+
</StyledRowLabel>
|
|
856
|
+
{/* An OPEN field always shows its short_desc — that's where the hint
|
|
857
|
+
is needed and there's no per-row button to reveal it (the edit
|
|
858
|
+
card does the same). The global descriptions toggle governs READ
|
|
859
|
+
rows only, so collapsing the field is what hides it again. */}
|
|
860
|
+
{labelShortDesc ?
|
|
861
|
+
<StyledLabelDesc
|
|
862
|
+
className='options-readfirst-label-desc'
|
|
863
|
+
size='small'
|
|
864
|
+
effect={{ opacity: 0.55 }}
|
|
865
|
+
>
|
|
866
|
+
{labelShortDesc}
|
|
867
|
+
</StyledLabelDesc>
|
|
868
|
+
: null}
|
|
869
|
+
</StyledLabelBlock>
|
|
662
870
|
<div
|
|
871
|
+
ref={editorRef}
|
|
663
872
|
style={{ minWidth: 0 }}
|
|
664
873
|
onKeyDown={(event) => {
|
|
665
874
|
if (event.key === 'Escape') {
|
|
@@ -678,16 +887,11 @@ export const CompactRow = memo(
|
|
|
678
887
|
message strip is visible. */}
|
|
679
888
|
{revertButton}
|
|
680
889
|
{clearValueButton}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
intent='success'
|
|
687
|
-
icon='CheckLine'
|
|
688
|
-
tooltip='Done'
|
|
689
|
-
onClick={collapse}
|
|
690
|
-
/>
|
|
890
|
+
{inlineOptionActions.map((action, index) =>
|
|
891
|
+
renderInjectedOptionAction(action, index)
|
|
892
|
+
)}
|
|
893
|
+
{moreMenu}
|
|
894
|
+
{closeExpandedFieldButton}
|
|
691
895
|
</StyledRowActions>
|
|
692
896
|
{/* Cluster node is rendered LAST (it's absolutely positioned, so DOM
|
|
693
897
|
order doesn't move it) — keeping it out of the leading cells lets
|
|
@@ -709,6 +913,27 @@ export const CompactRow = memo(
|
|
|
709
913
|
className={clusterBlockClass || undefined}
|
|
710
914
|
>
|
|
711
915
|
{editingRow}
|
|
916
|
+
{/* Focused editing for an INLINE row. The edit card wraps its editor
|
|
917
|
+
in <FocusedEditing>, which renders the field in place AND in the
|
|
918
|
+
modal; an inline row already renders its editor in the row grid,
|
|
919
|
+
so it mounts the modal alone. Without this the More menu's "Edit
|
|
920
|
+
fullscreen" set the state and nothing appeared — the modal simply
|
|
921
|
+
had no mount point on this branch. */}
|
|
922
|
+
{focusedEditing === optionName ?
|
|
923
|
+
<FocusedEditing
|
|
924
|
+
isFullscreen
|
|
925
|
+
modalOnly
|
|
926
|
+
onClose={() => setFocusedEditing(undefined)}
|
|
927
|
+
description={(schema?.display_name as string) || optionName}
|
|
928
|
+
>
|
|
929
|
+
<Description
|
|
930
|
+
longDescription={schema?.desc}
|
|
931
|
+
shortDescription={schema?.short_desc}
|
|
932
|
+
longDescriptionShownByDefault
|
|
933
|
+
/>
|
|
934
|
+
{renderOption(optionName, optionField)}
|
|
935
|
+
</FocusedEditing>
|
|
936
|
+
: null}
|
|
712
937
|
</StyledColumn>
|
|
713
938
|
);
|
|
714
939
|
}
|
|
@@ -749,7 +974,7 @@ export const CompactRow = memo(
|
|
|
749
974
|
}}
|
|
750
975
|
>
|
|
751
976
|
<StyledCardHeading>
|
|
752
|
-
<StyledCardLabel $color={
|
|
977
|
+
<StyledCardLabel $color={cKey}>
|
|
753
978
|
{(schema as { icon?: string } | undefined)?.icon || (schema as { image?: string } | undefined)?.image ?
|
|
754
979
|
<ReqoreIcon
|
|
755
980
|
icon={(schema as { icon?: string } | undefined)?.icon as never}
|
|
@@ -811,40 +1036,27 @@ export const CompactRow = memo(
|
|
|
811
1036
|
</ReqoreButton>
|
|
812
1037
|
);
|
|
813
1038
|
})}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
fixed
|
|
820
|
-
className='options-readfirst-fullscreen'
|
|
821
|
-
tooltip='Edit fullscreen'
|
|
822
|
-
onClick={() => setFocusedEditing(optionName)}
|
|
823
|
-
/>
|
|
824
|
-
{/* Clear-value sits between focus-edit and Done — the card analog of
|
|
825
|
-
the inline row's Clear. Empties the value (keeps the field). */}
|
|
1039
|
+
{inlineOptionActions.map((action, index) =>
|
|
1040
|
+
renderInjectedOptionAction(action, index)
|
|
1041
|
+
)}
|
|
1042
|
+
{/* Clear-value sits before the More menu — the card analog of the
|
|
1043
|
+
inline row's Clear. Empties the value (keeps the field). */}
|
|
826
1044
|
{hasValue && !readOnly ?
|
|
827
1045
|
<ReqoreButton
|
|
828
1046
|
size='small'
|
|
829
|
-
icon='
|
|
1047
|
+
icon='DeleteBinLine'
|
|
830
1048
|
minimal
|
|
831
1049
|
flat
|
|
832
1050
|
fixed
|
|
1051
|
+
intent='danger'
|
|
833
1052
|
className='options-readfirst-clear'
|
|
1053
|
+
aria-label='Clear value'
|
|
834
1054
|
tooltip='Clear value'
|
|
835
|
-
onClick={
|
|
1055
|
+
onClick={confirmClearValue}
|
|
836
1056
|
/>
|
|
837
1057
|
: null}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
icon='CheckLine'
|
|
841
|
-
intent='success'
|
|
842
|
-
fixed
|
|
843
|
-
className='options-readfirst-done'
|
|
844
|
-
onClick={() => toggleExpandedOption(optionName)}
|
|
845
|
-
>
|
|
846
|
-
{readOnly ? 'Close' : 'Done'}
|
|
847
|
-
</ReqoreButton>
|
|
1058
|
+
{moreMenu}
|
|
1059
|
+
{closeExpandedFieldButton}
|
|
848
1060
|
</ReqoreControlGroup>
|
|
849
1061
|
</div>
|
|
850
1062
|
{/* Same fullscreen focused-editing affordance as the classic cards —
|
|
@@ -869,6 +1081,15 @@ export const CompactRow = memo(
|
|
|
869
1081
|
|
|
870
1082
|
const formatted = formatOptionValue(optionField, schema);
|
|
871
1083
|
const empty = formatted === '';
|
|
1084
|
+
// Inline reasons shown on the value line: validation / dependency / one-of /
|
|
1085
|
+
// default-value hints, plus a read-only covered-by note (editable covered rows
|
|
1086
|
+
// carry that in their chip instead).
|
|
1087
|
+
const valueReasons: TInfoMsg[] = [
|
|
1088
|
+
...inlineMessages,
|
|
1089
|
+
...(empty && coveredByLabel ?
|
|
1090
|
+
[{ content: `Covered by “${coveredByLabel}”`, intent: 'success' }]
|
|
1091
|
+
: []),
|
|
1092
|
+
];
|
|
872
1093
|
// A hash row reveals its sub-fields as read-only sub-rows under a "view
|
|
873
1094
|
// more" disclosure; the row itself still expands the real editor on click.
|
|
874
1095
|
const valueType = getValueType(optionField, schema);
|
|
@@ -878,6 +1099,33 @@ export const CompactRow = memo(
|
|
|
878
1099
|
(schema as { ui_type?: string } | undefined)?.ui_type !== 'schema-definition' ?
|
|
879
1100
|
getHashEntries(optionField, schema)
|
|
880
1101
|
: [];
|
|
1102
|
+
// A LIST OF HASHES/objects gets the same expandable structured preview a hash
|
|
1103
|
+
// does — the flat "N items" summary can't convey object contents (and must
|
|
1104
|
+
// never print "[object Object]"). StructuredDataView unwraps each item's
|
|
1105
|
+
// {type,value} envelope. Plain scalar lists keep just their joined summary.
|
|
1106
|
+
const isHashList =
|
|
1107
|
+
!hidden &&
|
|
1108
|
+
(valueType === 'list' || valueType === 'free-list' || valueType === 'array') &&
|
|
1109
|
+
Array.isArray(optionField?.value) &&
|
|
1110
|
+
optionField.value.length > 0 &&
|
|
1111
|
+
optionField.value.some((item) => {
|
|
1112
|
+
const inner =
|
|
1113
|
+
item && typeof item === 'object' && 'value' in (item as Record<string, unknown>) ?
|
|
1114
|
+
(item as Record<string, unknown>).value
|
|
1115
|
+
: item;
|
|
1116
|
+
return inner !== null && typeof inner === 'object';
|
|
1117
|
+
});
|
|
1118
|
+
const showStructuredPreview = hashEntries.length > 0 || isHashList;
|
|
1119
|
+
// Multi-line code preview: a `code-editor` field with a non-empty string
|
|
1120
|
+
// value gets a `<pre>`-based code block under the row, capped by a
|
|
1121
|
+
// `ReqoreCollapsibleContent` so the collapsed height stays row-sized and
|
|
1122
|
+
// "Show more" reveals the rest. Sits in the same inset the hash/list
|
|
1123
|
+
// preview uses — mutually exclusive with `showStructuredPreview`.
|
|
1124
|
+
const showCodePreview =
|
|
1125
|
+
!hidden &&
|
|
1126
|
+
valueType === 'code-editor' &&
|
|
1127
|
+
typeof optionField?.value === 'string' &&
|
|
1128
|
+
optionField.value.length > 0;
|
|
881
1129
|
const typeLabel =
|
|
882
1130
|
showFieldTypes ?
|
|
883
1131
|
`<${(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}]` : ''}>`
|
|
@@ -936,8 +1184,7 @@ export const CompactRow = memo(
|
|
|
936
1184
|
flat
|
|
937
1185
|
compact
|
|
938
1186
|
icon='LockLine'
|
|
939
|
-
|
|
940
|
-
effect={{uppercase: true, spaced: 1}}
|
|
1187
|
+
tooltip='Disabled — depends on other fields. Click to locate them.'
|
|
941
1188
|
items={[
|
|
942
1189
|
{ divider: true, label: 'Unlocked by:', dividerAlign: 'left' } as IReqoreDropdownItem,
|
|
943
1190
|
...dependencyEntries.flatMap((entry): IReqoreDropdownItem[] => [
|
|
@@ -972,9 +1219,10 @@ export const CompactRow = memo(
|
|
|
972
1219
|
if (target?.classList?.contains('readfirst-row')) {
|
|
973
1220
|
readRowHeights.current[optionName] = Math.round(target.getBoundingClientRect().height);
|
|
974
1221
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1222
|
+
// Optional fields aren't "added" — every one is editable in place. Opening a
|
|
1223
|
+
// not-yet-set field just expands its editor (with an empty value); it joins
|
|
1224
|
+
// the form's output the moment a value is set (and moves to Set / Needs
|
|
1225
|
+
// attention), and drops back to Optional when cleared. No explicit add step.
|
|
978
1226
|
toggleExpandedOption(optionName);
|
|
979
1227
|
};
|
|
980
1228
|
|
|
@@ -1002,18 +1250,36 @@ export const CompactRow = memo(
|
|
|
1002
1250
|
rowStripeColor ?
|
|
1003
1251
|
({ ['--readfirst-stripe']: rowStripeColor } as React.CSSProperties)
|
|
1004
1252
|
: undefined;
|
|
1005
|
-
const blockWrapped = hashEntries.length > 0 || !!infoBlock;
|
|
1006
1253
|
|
|
1254
|
+
// "Focus" trailing status dot, derived from the SHARED status helper (the
|
|
1255
|
+
// same one FormEngine uses to bucket rows into the Needs-attention/Set/Optional
|
|
1256
|
+
// boxes — so the dot and the box can never disagree). Hidden (not-yet-added)
|
|
1257
|
+
// and dependency-locked rows show no dot (the add/lock affordance speaks for
|
|
1258
|
+
// them). worstIntent already folds in schema + validation messages.
|
|
1259
|
+
const cSuccess = (theme?.intents as Record<string, string> | undefined)?.success;
|
|
1260
|
+
const rowStatus = getReadFirstStatus({
|
|
1261
|
+
empty,
|
|
1262
|
+
required,
|
|
1263
|
+
covered: !!coveredByLabel || groupResolved,
|
|
1264
|
+
invalid: worstIntent === 'danger',
|
|
1265
|
+
warned: worstIntent === 'warning',
|
|
1266
|
+
});
|
|
1267
|
+
const dotStatus = hidden || fieldDisabled ? undefined : rowStatus;
|
|
1268
|
+
const dotColor =
|
|
1269
|
+
dotStatus === 'invalid' ? cDanger
|
|
1270
|
+
: dotStatus === 'todo' ? cWarning
|
|
1271
|
+
: dotStatus === 'set' ? cSuccess || cInfo
|
|
1272
|
+
: undefined;
|
|
1007
1273
|
const row = (
|
|
1008
1274
|
<div
|
|
1009
1275
|
key={optionName}
|
|
1010
1276
|
data-field={optionName}
|
|
1011
1277
|
role='button'
|
|
1012
1278
|
tabIndex={0}
|
|
1013
|
-
aria-label={
|
|
1014
|
-
className={`readfirst-row options-readfirst-value${hidden ? ' readfirst-row-hidden' : ''}${fieldDisabled ? ' readfirst-row-disabled' : ''}${isHighlighted ? ' readfirst-row-group-highlight' : ''}${isFlashed ? ' readfirst-row-flash' : ''}${
|
|
1279
|
+
aria-label={label}
|
|
1280
|
+
className={`readfirst-row options-readfirst-value${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 ? ' readfirst-row-tall' : ''}${clusterBlockClass ? ' ' + clusterBlockClass : ''}`}
|
|
1015
1281
|
aria-disabled={fieldDisabled || undefined}
|
|
1016
|
-
style={
|
|
1282
|
+
style={stripeStyle}
|
|
1017
1283
|
onClick={activate}
|
|
1018
1284
|
onKeyDown={(event) => {
|
|
1019
1285
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
@@ -1027,31 +1293,20 @@ export const CompactRow = memo(
|
|
|
1027
1293
|
<StyledLabelBlock>
|
|
1028
1294
|
<StyledRowLabel title={schema?.short_desc || undefined} $color={cKey}>
|
|
1029
1295
|
{rowChromeIcon}
|
|
1030
|
-
{label
|
|
1031
|
-
|
|
1296
|
+
{/* label + asterisk + help flow as ONE inline run, so the asterisk
|
|
1297
|
+
stays right after the last word even when the name wraps. */}
|
|
1298
|
+
<span className='options-readfirst-label-text' style={{ minWidth: 0 }}>
|
|
1299
|
+
{label}
|
|
1300
|
+
{required ?
|
|
1301
|
+
<ReqoreIcon icon='Asterisk' color='danger' size='10px' margin='left' marginSize='tiny' />
|
|
1302
|
+
: null}
|
|
1303
|
+
{helpIcon}
|
|
1304
|
+
</span>
|
|
1032
1305
|
{typeLabel ?
|
|
1033
1306
|
<ReqoreTag size='tiny' minimal label={typeLabel} labelEffect={{ opacity: 0.55 }} />
|
|
1034
1307
|
: null}
|
|
1035
|
-
{schema?.desc ?
|
|
1036
|
-
<ReqoreIcon
|
|
1037
|
-
icon='QuestionLine'
|
|
1038
|
-
size='12px'
|
|
1039
|
-
effect={{ opacity: 0.55 }}
|
|
1040
|
-
margin='left'
|
|
1041
|
-
marginSize={5}
|
|
1042
|
-
role='button'
|
|
1043
|
-
tabIndex={-1}
|
|
1044
|
-
aria-label='Help'
|
|
1045
|
-
className='options-readfirst-help'
|
|
1046
|
-
style={{ cursor: 'help' }}
|
|
1047
|
-
onClick={(event) => {
|
|
1048
|
-
event.stopPropagation();
|
|
1049
|
-
handleOptionLabelClick(optionName);
|
|
1050
|
-
}}
|
|
1051
|
-
/>
|
|
1052
|
-
: null}
|
|
1053
1308
|
</StyledRowLabel>
|
|
1054
|
-
{
|
|
1309
|
+
{showLabelDesc ?
|
|
1055
1310
|
<StyledLabelDesc
|
|
1056
1311
|
className='options-readfirst-label-desc'
|
|
1057
1312
|
size='small'
|
|
@@ -1063,22 +1318,84 @@ export const CompactRow = memo(
|
|
|
1063
1318
|
</StyledLabelBlock>
|
|
1064
1319
|
<StyledRowValue
|
|
1065
1320
|
title={!empty && !hidden && typeof formatted === 'string' ? formatted : undefined}
|
|
1066
|
-
|
|
1321
|
+
// A SET value reads at full key brightness so it stands out as the
|
|
1322
|
+
// actual data — crucial when stacked under a (muted) description on
|
|
1323
|
+
// mobile, where a dim value blends into the prose. The bold name still
|
|
1324
|
+
// outranks it; the empty dash stays faint.
|
|
1325
|
+
$color={empty || hidden ? `${cMuted}66` : cKey}
|
|
1067
1326
|
$empty={empty || hidden}
|
|
1068
1327
|
>
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1328
|
+
<span className='options-readfirst-valuetext'>
|
|
1329
|
+
{hidden || empty ? '—' : renderReadFirstValue(optionField, schema, formatted)}
|
|
1330
|
+
</span>
|
|
1331
|
+
{valueReasons.map((m, i) => (
|
|
1332
|
+
<span
|
|
1333
|
+
key={`${m.content}-${i}`}
|
|
1334
|
+
className='options-readfirst-reason'
|
|
1335
|
+
style={{ color: reasonColor(m.intent) }}
|
|
1336
|
+
>
|
|
1337
|
+
{m.content}
|
|
1338
|
+
</span>
|
|
1339
|
+
))}
|
|
1340
|
+
{/* Dedicated schema message PANELS render right here, directly under the
|
|
1341
|
+
value (full width of the value column) — never pushed down by the
|
|
1342
|
+
label's short_desc. */}
|
|
1343
|
+
{panelMessages.length ?
|
|
1344
|
+
<div className='options-readfirst-info-panel'>{panelMessages.map(renderInfoStrip)}</div>
|
|
1345
|
+
: null}
|
|
1346
|
+
{/* The structured hash/list preview also lives in the value cell, so it
|
|
1347
|
+
starts directly under the value summary ("N fields"), not below the
|
|
1348
|
+
label's short_desc. */}
|
|
1349
|
+
{showStructuredPreview ?
|
|
1350
|
+
// The inset lives inside the row now, so a click on a value chip would
|
|
1351
|
+
// bubble to the row's onClick and fire activate() a SECOND time (the
|
|
1352
|
+
// chip's onItemClick already calls it) — toggling the editor shut again.
|
|
1353
|
+
// Stop the bubble here; the chip's own handler still opens the editor.
|
|
1354
|
+
<StyledRowInset
|
|
1355
|
+
className='options-readfirst-inset'
|
|
1356
|
+
onClick={(e) => e.stopPropagation()}
|
|
1357
|
+
>
|
|
1358
|
+
<ReqoreCollapsibleContent
|
|
1359
|
+
maxCollapsedHeight={96}
|
|
1360
|
+
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
1361
|
+
>
|
|
1362
|
+
<div className='options-readfirst-structured'>
|
|
1363
|
+
<StructuredDataView
|
|
1364
|
+
value={optionField?.value}
|
|
1365
|
+
collapsibleRoot={false}
|
|
1366
|
+
showTypes={showFieldTypes}
|
|
1367
|
+
defaultExpandDepth={2}
|
|
1368
|
+
onItemClick={() => activate()}
|
|
1369
|
+
/>
|
|
1370
|
+
</div>
|
|
1371
|
+
</ReqoreCollapsibleContent>
|
|
1372
|
+
</StyledRowInset>
|
|
1373
|
+
: null}
|
|
1374
|
+
{/* Code-editor preview: same inset placement as hash/list — a monospace
|
|
1375
|
+
`<pre>` block wrapped in `ReqoreCollapsibleContent` so the collapsed
|
|
1376
|
+
row stays row-height while multi-line source reveals via "Show more".
|
|
1377
|
+
Stopping propagation here (like the structured inset) — a click on
|
|
1378
|
+
the collapse button must not also open the field editor. */}
|
|
1379
|
+
{showCodePreview ?
|
|
1380
|
+
<StyledRowInset
|
|
1381
|
+
className='options-readfirst-inset options-readfirst-code-inset'
|
|
1382
|
+
onClick={(e) => e.stopPropagation()}
|
|
1383
|
+
>
|
|
1384
|
+
<ReqoreCollapsibleContent
|
|
1385
|
+
maxCollapsedHeight={96}
|
|
1386
|
+
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
1387
|
+
>
|
|
1388
|
+
<StyledCodePreview
|
|
1389
|
+
className='options-readfirst-code'
|
|
1390
|
+
$bg={cHover}
|
|
1391
|
+
$border={`${cDivider}88`}
|
|
1392
|
+
$fg={cKey}
|
|
1393
|
+
>
|
|
1394
|
+
{optionField.value as string}
|
|
1395
|
+
</StyledCodePreview>
|
|
1396
|
+
</ReqoreCollapsibleContent>
|
|
1397
|
+
</StyledRowInset>
|
|
1398
|
+
: null}
|
|
1082
1399
|
</StyledRowValue>
|
|
1083
1400
|
<StyledRowActions>
|
|
1084
1401
|
{/* Column discipline (table treatment): variable-width chips lead and
|
|
@@ -1093,63 +1410,24 @@ export const CompactRow = memo(
|
|
|
1093
1410
|
the "One of"/"Covers" chip — keep only "Covered by <X>", the one fact
|
|
1094
1411
|
the rail can't show. Non-clustered members (split-across-panels or
|
|
1095
1412
|
narrow mode, where there's no rail) keep the full chip as a fallback. */}
|
|
1096
|
-
{!hidden && !fieldDisabled &&
|
|
1413
|
+
{!hidden && !fieldDisabled && !clustered && !coveredByLabel ? requiredGroupChip : null}
|
|
1097
1414
|
{!hidden ? dependsOnChip : null}
|
|
1098
1415
|
{draftChip}
|
|
1099
|
-
{
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
tooltip='Revert changes'
|
|
1108
|
-
onClick={(e: React.MouseEvent) => {
|
|
1109
|
-
e.stopPropagation();
|
|
1110
|
-
handleValueChange(
|
|
1111
|
-
optionName,
|
|
1112
|
-
originalValue.current?.[optionName]?.value,
|
|
1113
|
-
originalValue.current?.[optionName]?.type
|
|
1114
|
-
);
|
|
1115
|
-
}}
|
|
1116
|
-
/>
|
|
1117
|
-
: null}
|
|
1118
|
-
{removable && !hidden ?
|
|
1119
|
-
<ReqoreButton
|
|
1120
|
-
className='readfirst-action'
|
|
1121
|
-
size='small'
|
|
1122
|
-
flat
|
|
1123
|
-
minimal
|
|
1124
|
-
intent='danger'
|
|
1125
|
-
icon='DeleteBinLine'
|
|
1126
|
-
tooltip='Remove field'
|
|
1127
|
-
onClick={(e: React.MouseEvent) => {
|
|
1128
|
-
e.stopPropagation();
|
|
1129
|
-
confirmAction({
|
|
1130
|
-
title: 'Remove field',
|
|
1131
|
-
onConfirm: () => removeSelectedOption(optionName),
|
|
1132
|
-
});
|
|
1133
|
-
}}
|
|
1134
|
-
/>
|
|
1135
|
-
: null}
|
|
1136
|
-
{/* Lock/add slot BEFORE the info slot so the ⓘ keeps the same far-right
|
|
1137
|
-
x on every row — a disabled field's lock sits to the ⓘ's left rather
|
|
1138
|
-
than pushing it inward. ADD for a hidden field; a plain LOCK for a
|
|
1139
|
-
field disabled for a non-dependency reason. Dependency-locked fields
|
|
1140
|
-
show the "Depends on" chip in the chips area instead; the whole row is
|
|
1141
|
-
click-to-edit, so there's no hover edit pencil. */}
|
|
1142
|
-
{hidden || (fieldDisabled && !dependencyEntries.length) ?
|
|
1416
|
+
{/* Remove-field lives in the expanded editor's "More" (⋮) menu now — no
|
|
1417
|
+
standalone delete button on the read row. */}
|
|
1418
|
+
{/* Lock slot BEFORE the info slot so the ⓘ keeps the same far-right x on
|
|
1419
|
+
every row — a disabled field's lock sits to the ⓘ's left rather than
|
|
1420
|
+
pushing it inward. (Not-yet-set optional fields show NO marker now —
|
|
1421
|
+
they're just editable in place, like every other row. Dependency-
|
|
1422
|
+
locked fields show the "Depends on" chip in the chips area instead.) */}
|
|
1423
|
+
{fieldDisabled && !dependencyEntries.length ?
|
|
1143
1424
|
<StyledActionSlot className='options-readfirst-trailing-slot' $width={18}>
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
<ReqoreIcon className='options-readfirst-locked' icon='LockLine' size='14px' />
|
|
1151
|
-
</span>
|
|
1152
|
-
}
|
|
1425
|
+
<span
|
|
1426
|
+
title={fieldDisabledReason}
|
|
1427
|
+
style={{ display: 'inline-flex', opacity: 0.45 }}
|
|
1428
|
+
>
|
|
1429
|
+
<ReqoreIcon className='options-readfirst-locked' icon='LockLine' size='14px' />
|
|
1430
|
+
</span>
|
|
1153
1431
|
</StyledActionSlot>
|
|
1154
1432
|
: null}
|
|
1155
1433
|
{infoToggle ?
|
|
@@ -1157,61 +1435,61 @@ export const CompactRow = memo(
|
|
|
1157
1435
|
{infoToggle}
|
|
1158
1436
|
</StyledActionSlot>
|
|
1159
1437
|
: null}
|
|
1438
|
+
{inlineOptionActions.map((action, index) =>
|
|
1439
|
+
renderInjectedOptionAction(action, index, 'tiny')
|
|
1440
|
+
)}
|
|
1441
|
+
{injectedOptionActionMenuItems.length ?
|
|
1442
|
+
<StyledActionSlot
|
|
1443
|
+
className='options-readfirst-actions-slot'
|
|
1444
|
+
$width={26}
|
|
1445
|
+
// The read row opens the editor when clicked. Without this the tap
|
|
1446
|
+
// that opens this menu also expands the row, which unmounts the
|
|
1447
|
+
// menu — on touch that made the collapsed actions unreachable,
|
|
1448
|
+
// the exact problem the collapse exists to solve.
|
|
1449
|
+
onClick={(event: React.MouseEvent<HTMLElement>) => event.stopPropagation()}
|
|
1450
|
+
>
|
|
1451
|
+
<ReqoreDropdown
|
|
1452
|
+
className='options-injected-actions-menu'
|
|
1453
|
+
icon='MoreFill'
|
|
1454
|
+
flat
|
|
1455
|
+
minimal
|
|
1456
|
+
fixed
|
|
1457
|
+
size='tiny'
|
|
1458
|
+
tooltip='Field actions'
|
|
1459
|
+
items={injectedOptionActionMenuItems}
|
|
1460
|
+
/>
|
|
1461
|
+
</StyledActionSlot>
|
|
1462
|
+
: null}
|
|
1463
|
+
{/* The revert affordance lives in the status-dot column (a changed field
|
|
1464
|
+
swaps its dot for the revert icon) so it sits at the same fixed x as
|
|
1465
|
+
the dot — out of the value's way instead of floating over it. */}
|
|
1466
|
+
<StyledActionSlot className='options-readfirst-statusdot-slot' $width={12}>
|
|
1467
|
+
{changed && !readOnly && !hidden ?
|
|
1468
|
+
<ReqoreButton
|
|
1469
|
+
className='options-readfirst-revert'
|
|
1470
|
+
size='tiny'
|
|
1471
|
+
flat
|
|
1472
|
+
minimal
|
|
1473
|
+
compact
|
|
1474
|
+
icon='HistoryLine'
|
|
1475
|
+
tooltip='Revert changes'
|
|
1476
|
+
onClick={(e: React.MouseEvent) => {
|
|
1477
|
+
e.stopPropagation();
|
|
1478
|
+
handleValueChange(
|
|
1479
|
+
optionName,
|
|
1480
|
+
originalValue.current?.[optionName]?.value,
|
|
1481
|
+
originalValue.current?.[optionName]?.type
|
|
1482
|
+
);
|
|
1483
|
+
}}
|
|
1484
|
+
/>
|
|
1485
|
+
: dotColor ?
|
|
1486
|
+
<StyledStatusDot $color={dotColor} $ring={dotStatus !== 'set'} aria-hidden />
|
|
1487
|
+
: null}
|
|
1488
|
+
</StyledActionSlot>
|
|
1160
1489
|
</StyledRowActions>
|
|
1161
1490
|
</div>
|
|
1162
1491
|
);
|
|
1163
1492
|
|
|
1164
|
-
if (hashEntries.length) {
|
|
1165
|
-
return (
|
|
1166
|
-
<StyledColumn
|
|
1167
|
-
key={optionName}
|
|
1168
|
-
data-field={optionName}
|
|
1169
|
-
className={`options-readfirst-hash-row${clusterBlockClass ? ' ' + clusterBlockClass : ''}`}
|
|
1170
|
-
style={stripeStyle}
|
|
1171
|
-
>
|
|
1172
|
-
{row}
|
|
1173
|
-
<StyledRowInset className='options-readfirst-inset'>
|
|
1174
|
-
<ReqoreCollapsibleContent
|
|
1175
|
-
maxCollapsedHeight={96}
|
|
1176
|
-
buttonProps={{ className: 'options-readfirst-viewmore' }}
|
|
1177
|
-
>
|
|
1178
|
-
{/* The IDE workflow-orders renderer (ReqoreDataView): a nested,
|
|
1179
|
-
type-coloured tree. Section summaries own their
|
|
1180
|
-
expand/collapse clicks, but clicking a VALUE chip opens the
|
|
1181
|
-
hash's editor. The Fields-menu "Show field types" toggle also
|
|
1182
|
-
drives the per-scalar type chips here. Depth 2 = root + first
|
|
1183
|
-
level open; deeper nests start collapsed so the preview stays
|
|
1184
|
-
short before the fade's "Show more". */}
|
|
1185
|
-
<div className='options-readfirst-structured'>
|
|
1186
|
-
<StructuredDataView
|
|
1187
|
-
value={optionField?.value}
|
|
1188
|
-
collapsibleRoot={false}
|
|
1189
|
-
showTypes={showFieldTypes}
|
|
1190
|
-
defaultExpandDepth={2}
|
|
1191
|
-
onItemClick={() => activate()}
|
|
1192
|
-
/>
|
|
1193
|
-
</div>
|
|
1194
|
-
</ReqoreCollapsibleContent>
|
|
1195
|
-
</StyledRowInset>
|
|
1196
|
-
{infoBlock}
|
|
1197
|
-
</StyledColumn>
|
|
1198
|
-
);
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
if (infoBlock) {
|
|
1202
|
-
return (
|
|
1203
|
-
<StyledColumn
|
|
1204
|
-
key={optionName}
|
|
1205
|
-
data-field={optionName}
|
|
1206
|
-
className={`options-readfirst-info-row${clusterBlockClass ? ' ' + clusterBlockClass : ''}`}
|
|
1207
|
-
style={stripeStyle}
|
|
1208
|
-
>
|
|
1209
|
-
{row}
|
|
1210
|
-
{infoBlock}
|
|
1211
|
-
</StyledColumn>
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
1493
|
return row;
|
|
1216
1494
|
}
|
|
1217
1495
|
);
|