@qoretechnologies/reqraft 0.10.65 → 0.10.66-pr.145.g124fc92
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/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +130 -25
- 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 +78 -15
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +48 -1
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +260 -99
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.d.ts +18 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +22 -3
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +27 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +5 -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 +12 -0
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +72 -39
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +3 -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/formAppearance.d.ts +48 -0
- package/dist/components/form/engine/formAppearance.d.ts.map +1 -0
- package/dist/components/form/engine/formAppearance.js +50 -0
- package/dist/components/form/engine/formAppearance.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +14 -2
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +6 -2
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rowFocus.d.ts +9 -1
- package/dist/components/form/engine/rowFocus.d.ts.map +1 -1
- package/dist/components/form/engine/rowFocus.js +17 -5
- package/dist/components/form/engine/rowFocus.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +21 -0
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/fields/ChoiceGroupHeading.d.ts +21 -0
- package/dist/components/form/fields/ChoiceGroupHeading.d.ts.map +1 -0
- package/dist/components/form/fields/ChoiceGroupHeading.js +33 -0
- package/dist/components/form/fields/ChoiceGroupHeading.js.map +1 -0
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +13 -11
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +20 -7
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/allowed-values/savedValues.d.ts +30 -0
- package/dist/components/form/fields/allowed-values/savedValues.d.ts.map +1 -0
- package/dist/components/form/fields/allowed-values/savedValues.js +18 -0
- package/dist/components/form/fields/allowed-values/savedValues.js.map +1 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +18 -8
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts +7 -1
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +9 -5
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +6 -5
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/number/Number.d.ts +4 -1
- package/dist/components/form/fields/number/Number.d.ts.map +1 -1
- package/dist/components/form/fields/number/Number.js +13 -5
- package/dist/components/form/fields/number/Number.js.map +1 -1
- package/dist/components/form/fields/radio-group/RadioGroup.d.ts.map +1 -1
- package/dist/components/form/fields/radio-group/RadioGroup.js +13 -7
- package/dist/components/form/fields/radio-group/RadioGroup.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +71 -42
- package/dist/components/form/fields/template/TemplateField.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.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.js +2 -0
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +3 -2
- 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 +2 -1
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
- package/dist/components/supportTicket/ReferencePicker.js +19 -13
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketHeader.js +4 -1
- package/dist/components/supportTicket/TicketHeader.js.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.js +29 -10
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
- package/dist/components/supportTicket/useLoadedImages.d.ts +10 -0
- package/dist/components/supportTicket/useLoadedImages.d.ts.map +1 -1
- package/dist/components/supportTicket/useLoadedImages.js +63 -1
- 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 +6 -2
- 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 +10 -16
- package/dist/components/ticketThread/TicketThread.js.map +1 -1
- package/dist/helpers/allowedValues.d.ts +12 -0
- package/dist/helpers/allowedValues.d.ts.map +1 -1
- package/dist/helpers/allowedValues.js +20 -1
- package/dist/helpers/allowedValues.js.map +1 -1
- package/dist/helpers/fieldDescription.d.ts +16 -0
- package/dist/helpers/fieldDescription.d.ts.map +1 -0
- package/dist/helpers/fieldDescription.js +40 -0
- package/dist/helpers/fieldDescription.js.map +1 -0
- package/dist/helpers/singleLineString.d.ts.map +1 -1
- package/dist/helpers/singleLineString.js +1 -0
- package/dist/helpers/singleLineString.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/engine/CompactRow.tsx +143 -23
- package/src/components/form/engine/CompactToolbar.tsx +85 -16
- package/src/components/form/engine/FormEngine.stories.tsx +25 -13
- package/src/components/form/engine/FormEngine.tsx +367 -81
- package/src/components/form/engine/FormEngineAppearance.stories.tsx +540 -0
- package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +5 -1
- package/src/components/form/engine/OptionFieldMessages.tsx +73 -2
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +17 -4
- package/src/components/form/engine/compactRowContext.ts +5 -0
- package/src/components/form/engine/compactRowStyles.ts +218 -38
- package/src/components/form/engine/compactToolbarContext.ts +3 -0
- package/src/components/form/engine/formAppearance.ts +78 -0
- package/src/components/form/engine/readFirst.ts +16 -3
- package/src/components/form/engine/rowFocus.ts +17 -6
- package/src/components/form/expressions/renderExpressionToText.ts +15 -0
- package/src/components/form/fields/ChoiceGroupHeading.tsx +38 -0
- package/src/components/form/fields/Field.tsx +11 -9
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +21 -9
- package/src/components/form/fields/allowed-values/savedValues.ts +41 -0
- package/src/components/form/fields/auto/AutoFormField.tsx +17 -4
- package/src/components/form/fields/long-string/LongString.stories.tsx +27 -0
- package/src/components/form/fields/long-string/LongString.tsx +14 -3
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +5 -4
- package/src/components/form/fields/number/Number.tsx +19 -4
- package/src/components/form/fields/radio-group/RadioGroup.tsx +13 -7
- package/src/components/form/fields/template/TemplateField.tsx +32 -4
- package/src/components/form/index.tsx +2 -0
- package/src/components/imageLightbox/ImageLightbox.tsx +2 -0
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +3 -2
- package/src/components/supportTicket/ReferencePicker.stories.tsx +0 -4
- package/src/components/supportTicket/ReferencePicker.tsx +22 -12
- package/src/components/supportTicket/TicketConversation.stories.tsx +0 -1
- package/src/components/supportTicket/TicketHeader.tsx +2 -1
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +24 -21
- package/src/components/supportTicket/TicketReplyBox.tsx +27 -5
- package/src/components/supportTicket/useLoadedImages.ts +20 -0
- package/src/components/ticketReferences/TicketReferences.stories.tsx +26 -0
- package/src/components/ticketReferences/TicketReferences.tsx +12 -4
- package/src/components/ticketThread/TicketThread.stories.tsx +71 -2
- package/src/components/ticketThread/TicketThread.tsx +15 -28
- package/src/helpers/allowedValues.ts +19 -0
- package/src/helpers/fieldDescription.ts +39 -0
- package/src/helpers/singleLineString.ts +1 -0
|
@@ -27,6 +27,7 @@ import { useContextSelector } from 'use-context-selector';
|
|
|
27
27
|
import { hasAllDependenciesFullfilled, parseDependency } from '../../../helpers/validations';
|
|
28
28
|
import { findTemplate, isValueTemplate } from '../../../helpers/templates';
|
|
29
29
|
import { getDefaultValue, richtextToSegments, richtextToString } from '../../../helpers/common';
|
|
30
|
+
import { fieldHasLongDescription } from '../../../helpers/fieldDescription';
|
|
30
31
|
import { hasTemplateText, templateTextSegments } from '../../../helpers/templateText';
|
|
31
32
|
import { ReadOnlyTemplateTag } from '../fields/template/ReadOnlyTemplateTag';
|
|
32
33
|
import { TemplateText } from '../fields/template/TemplateText';
|
|
@@ -35,6 +36,7 @@ import { Description } from '../../Description';
|
|
|
35
36
|
import { useMarkdownRenderer } from '../../Description/markdownRendererContext';
|
|
36
37
|
import { FocusedEditing } from '../../FocusedEditing';
|
|
37
38
|
import { CompactRowContext } from './compactRowContext';
|
|
39
|
+
import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL, useIsCalmForm } from './formAppearance';
|
|
38
40
|
import { revealRow } from './revealRow';
|
|
39
41
|
import {
|
|
40
42
|
StyledActionSlot,
|
|
@@ -192,8 +194,17 @@ export const CompactRow = memo(
|
|
|
192
194
|
clusterLast?: boolean;
|
|
193
195
|
}) => {
|
|
194
196
|
const readOnly = useContextSelector(CompactRowContext, (v) => v.readOnly);
|
|
197
|
+
// The look only — see `TFormEngineAppearance`. Nothing a row DOES reads it.
|
|
198
|
+
const calm = useIsCalmForm();
|
|
195
199
|
const commitMode = useContextSelector(CompactRowContext, (v) => v.commitMode);
|
|
196
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
|
+
);
|
|
197
208
|
|
|
198
209
|
// An absorbed field keeps its own name — it is a different field, not a
|
|
199
210
|
// property of its host — so the label comes from its schema exactly as it
|
|
@@ -296,6 +307,11 @@ export const CompactRow = memo(
|
|
|
296
307
|
const cDanger = useContextSelector(CompactRowContext, (v) => v.cDanger);
|
|
297
308
|
const cWarning = useContextSelector(CompactRowContext, (v) => v.cWarning);
|
|
298
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;
|
|
299
315
|
// A markdown value renders as a document in the row's inset; the host's
|
|
300
316
|
// renderer when it supplied one, reqraft's own otherwise.
|
|
301
317
|
|
|
@@ -402,6 +418,11 @@ export const CompactRow = memo(
|
|
|
402
418
|
}
|
|
403
419
|
|
|
404
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
|
+
}
|
|
405
426
|
const truthy = field?.value === true || field?.value === 'true';
|
|
406
427
|
return (
|
|
407
428
|
<ReqoreTag
|
|
@@ -701,8 +722,11 @@ export const CompactRow = memo(
|
|
|
701
722
|
);
|
|
702
723
|
};
|
|
703
724
|
const required = !!(schema?.required || schema?.required_groups);
|
|
704
|
-
// The asterisk says "you have to fill this in". Read-only, nobody does.
|
|
705
|
-
|
|
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;
|
|
706
730
|
const removable =
|
|
707
731
|
!readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
|
|
708
732
|
const changed = !hidden && !readOnly && hasOptionChanged(optionField?.value, optionName);
|
|
@@ -767,8 +791,15 @@ export const CompactRow = memo(
|
|
|
767
791
|
minimal
|
|
768
792
|
flat
|
|
769
793
|
compact
|
|
770
|
-
effect={{ uppercase: true, spaced: 1 }}
|
|
771
|
-
|
|
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
|
+
}
|
|
772
803
|
icon={groupResolved ? 'CheckLine' : 'LinkM'}
|
|
773
804
|
label={
|
|
774
805
|
!groupResolved ? 'One of'
|
|
@@ -921,16 +952,39 @@ export const CompactRow = memo(
|
|
|
921
952
|
const editor = editorRef.current;
|
|
922
953
|
if (!editor) return undefined;
|
|
923
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;
|
|
924
961
|
const takeCaret = (): boolean => {
|
|
925
962
|
if (settled) return true;
|
|
926
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
|
+
}
|
|
927
980
|
if (active && active !== document.body && !rowRef.current?.contains(active)) {
|
|
928
981
|
// Someone else holds the caret — a field the reader moved into while
|
|
929
982
|
// this row's editor was still arriving. Leave it where it is.
|
|
930
983
|
settled = true;
|
|
931
984
|
return true;
|
|
932
985
|
}
|
|
933
|
-
|
|
986
|
+
// A card hands the caret only to where text goes (see `findRowFocusTarget`).
|
|
987
|
+
const control = findRowFocusTarget(editor, { textEntryOnly: !inlineEditable });
|
|
934
988
|
if (!control) return false;
|
|
935
989
|
// The least scroll that shows the row clear of the pinned box header,
|
|
936
990
|
// and none when it is already in view (see `revealRow`).
|
|
@@ -940,6 +994,9 @@ export const CompactRow = memo(
|
|
|
940
994
|
// `preventScroll`, because the reveal above has already put the row
|
|
941
995
|
// where it belongs; letting focus scroll as well moves it twice.
|
|
942
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;
|
|
943
1000
|
/* …and if the control the row offers is a PICKER, open it.
|
|
944
1001
|
*
|
|
945
1002
|
* A closed picker prints the value it holds, which is exactly what the
|
|
@@ -957,6 +1014,13 @@ export const CompactRow = memo(
|
|
|
957
1014
|
if (openedByAuthor.current && inlineEditable && isClosedPickerTrigger(control)) {
|
|
958
1015
|
openedByAuthor.current = false;
|
|
959
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;
|
|
960
1024
|
}
|
|
961
1025
|
settled = true;
|
|
962
1026
|
return true;
|
|
@@ -965,7 +1029,9 @@ export const CompactRow = memo(
|
|
|
965
1029
|
const observer = new MutationObserver(() => {
|
|
966
1030
|
if (takeCaret()) observer.disconnect();
|
|
967
1031
|
});
|
|
968
|
-
|
|
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 });
|
|
969
1035
|
return () => observer.disconnect();
|
|
970
1036
|
}, [isExpanded, optionName, inlineEditable]);
|
|
971
1037
|
|
|
@@ -1150,7 +1216,7 @@ export const CompactRow = memo(
|
|
|
1150
1216
|
fixed
|
|
1151
1217
|
flat
|
|
1152
1218
|
compact
|
|
1153
|
-
effect={{ uppercase: true, spaced: 1 }}
|
|
1219
|
+
effect={calm ? undefined : { uppercase: true, spaced: 1 }}
|
|
1154
1220
|
/>
|
|
1155
1221
|
: null;
|
|
1156
1222
|
const isPassiveCloseAction = fixedAllowedValueOption;
|
|
@@ -1174,7 +1240,7 @@ export const CompactRow = memo(
|
|
|
1174
1240
|
minimal
|
|
1175
1241
|
fixed
|
|
1176
1242
|
icon='CloseLine'
|
|
1177
|
-
intent='warning'
|
|
1243
|
+
intent={calm ? undefined : 'warning'}
|
|
1178
1244
|
// "Cancel edit", not "Cancel": a bare Cancel collides with the one
|
|
1179
1245
|
// in Reqore's confirmation modal, and this one is specifically about
|
|
1180
1246
|
// abandoning the edit rather than dismissing a dialog.
|
|
@@ -1195,7 +1261,9 @@ export const CompactRow = memo(
|
|
|
1195
1261
|
<ReqoreButton
|
|
1196
1262
|
className='options-readfirst-done'
|
|
1197
1263
|
size='small'
|
|
1198
|
-
|
|
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}
|
|
1199
1267
|
flat={isPassiveCloseAction}
|
|
1200
1268
|
minimal={isPassiveCloseAction}
|
|
1201
1269
|
fixed
|
|
@@ -1236,6 +1304,10 @@ export const CompactRow = memo(
|
|
|
1236
1304
|
allOptions: availableOptions,
|
|
1237
1305
|
getType: getTypeForOption,
|
|
1238
1306
|
isRendererOnly,
|
|
1307
|
+
calm,
|
|
1308
|
+
readOnly,
|
|
1309
|
+
// Until the person has been to the field, its requirement is stated plainly.
|
|
1310
|
+
requirementPending: !visited,
|
|
1239
1311
|
}).map((m) => ({ intent: m.intent as string, content: String(m.label) }))
|
|
1240
1312
|
: [];
|
|
1241
1313
|
// TWO display channels, matching the Focus prototype:
|
|
@@ -1274,7 +1346,7 @@ export const CompactRow = memo(
|
|
|
1274
1346
|
// with no route to the field's description at all. Stops propagation because
|
|
1275
1347
|
// both labels are click targets themselves (the row expands / collapses).
|
|
1276
1348
|
const helpIcon =
|
|
1277
|
-
schema
|
|
1349
|
+
fieldHasLongDescription(schema) ?
|
|
1278
1350
|
<ReqoreIcon
|
|
1279
1351
|
icon='QuestionLine'
|
|
1280
1352
|
size='12px'
|
|
@@ -1317,7 +1389,14 @@ export const CompactRow = memo(
|
|
|
1317
1389
|
size='small'
|
|
1318
1390
|
opaque={false}
|
|
1319
1391
|
flat
|
|
1320
|
-
|
|
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
|
+
}
|
|
1321
1400
|
title={m.title}
|
|
1322
1401
|
style={{ marginBottom: 8 }}
|
|
1323
1402
|
>
|
|
@@ -1493,7 +1572,7 @@ export const CompactRow = memo(
|
|
|
1493
1572
|
tabIndex={0}
|
|
1494
1573
|
aria-label={`Collapse ${label}`}
|
|
1495
1574
|
title={schema?.short_desc || undefined}
|
|
1496
|
-
$color={
|
|
1575
|
+
$color={cLabel}
|
|
1497
1576
|
$pointer
|
|
1498
1577
|
onClick={collapse}
|
|
1499
1578
|
onKeyDown={(event) => {
|
|
@@ -1504,7 +1583,7 @@ export const CompactRow = memo(
|
|
|
1504
1583
|
}}
|
|
1505
1584
|
>
|
|
1506
1585
|
{label}
|
|
1507
|
-
{required ?
|
|
1586
|
+
{required && !calm ?
|
|
1508
1587
|
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1509
1588
|
: null}
|
|
1510
1589
|
{helpIcon}
|
|
@@ -1526,6 +1605,7 @@ export const CompactRow = memo(
|
|
|
1526
1605
|
</StyledLabelBlock>
|
|
1527
1606
|
<div
|
|
1528
1607
|
ref={editorRef}
|
|
1608
|
+
className='options-readfirst-row-editor'
|
|
1529
1609
|
style={{ minWidth: 0 }}
|
|
1530
1610
|
onKeyDown={(event) => {
|
|
1531
1611
|
if (event.key === 'Escape') {
|
|
@@ -1664,6 +1744,7 @@ export const CompactRow = memo(
|
|
|
1664
1744
|
return (
|
|
1665
1745
|
<StyledEditCard
|
|
1666
1746
|
key={optionName}
|
|
1747
|
+
ref={rowRef}
|
|
1667
1748
|
data-field={optionName}
|
|
1668
1749
|
className={
|
|
1669
1750
|
COMPACT_SINGLE_VALUE_TYPES.has(editType) ?
|
|
@@ -1672,6 +1753,7 @@ export const CompactRow = memo(
|
|
|
1672
1753
|
}
|
|
1673
1754
|
$bg={cHover}
|
|
1674
1755
|
$border={schemaIntentColor ? `${schemaIntentColor}66` : `${cInfo}66`}
|
|
1756
|
+
$calm={calm}
|
|
1675
1757
|
>
|
|
1676
1758
|
<div
|
|
1677
1759
|
style={{
|
|
@@ -1682,7 +1764,7 @@ export const CompactRow = memo(
|
|
|
1682
1764
|
}}
|
|
1683
1765
|
>
|
|
1684
1766
|
<StyledCardHeading>
|
|
1685
|
-
<StyledCardLabel $color={
|
|
1767
|
+
<StyledCardLabel $color={cLabel}>
|
|
1686
1768
|
{(
|
|
1687
1769
|
(schema as { icon?: string } | undefined)?.icon ||
|
|
1688
1770
|
(schema as { image?: string } | undefined)?.image
|
|
@@ -1695,7 +1777,7 @@ export const CompactRow = memo(
|
|
|
1695
1777
|
: null}
|
|
1696
1778
|
<span>
|
|
1697
1779
|
{label}
|
|
1698
|
-
{required ?
|
|
1780
|
+
{required && !calm ?
|
|
1699
1781
|
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1700
1782
|
: null}
|
|
1701
1783
|
{/* The `?` belongs here too. The read row and the inline
|
|
@@ -1802,6 +1884,11 @@ export const CompactRow = memo(
|
|
|
1802
1884
|
keystroke a silent commit. Same handler, same `cancelEdit()`, so
|
|
1803
1885
|
the two shapes cannot diverge. */}
|
|
1804
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'
|
|
1805
1892
|
style={{ display: 'contents' }}
|
|
1806
1893
|
onKeyDown={(event) => {
|
|
1807
1894
|
if (event.key === 'Escape') {
|
|
@@ -1861,7 +1948,7 @@ export const CompactRow = memo(
|
|
|
1861
1948
|
const valueReasons: TInfoMsg[] = [
|
|
1862
1949
|
...inlineMessages,
|
|
1863
1950
|
...(empty && coveredByLabel ?
|
|
1864
|
-
[{ content: `Covered by “${coveredByLabel}”`, intent: 'success' }]
|
|
1951
|
+
[{ content: `Covered by “${coveredByLabel}”`, intent: calm ? undefined : 'success' }]
|
|
1865
1952
|
: []),
|
|
1866
1953
|
];
|
|
1867
1954
|
// A hash row reveals its sub-fields as read-only sub-rows under a "view
|
|
@@ -2207,10 +2294,28 @@ export const CompactRow = memo(
|
|
|
2207
2294
|
covered: !!coveredByLabel || groupResolved,
|
|
2208
2295
|
invalid: worstIntent === 'danger',
|
|
2209
2296
|
warned: worstIntent === 'warning',
|
|
2297
|
+
visited,
|
|
2210
2298
|
});
|
|
2211
2299
|
// No dot at all in a read-only form: the dot reports progress on the
|
|
2212
2300
|
// form, and a read view has no progress to report.
|
|
2213
|
-
|
|
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;
|
|
2214
2319
|
const dotColor =
|
|
2215
2320
|
dotStatus === 'invalid' ? cDanger
|
|
2216
2321
|
: dotStatus === 'todo' ? cWarning
|
|
@@ -2252,7 +2357,7 @@ export const CompactRow = memo(
|
|
|
2252
2357
|
>
|
|
2253
2358
|
{clusterNode}
|
|
2254
2359
|
<StyledLabelBlock className='options-readfirst-label-block'>
|
|
2255
|
-
<StyledRowLabel title={schema?.short_desc || undefined} $color={
|
|
2360
|
+
<StyledRowLabel title={schema?.short_desc || undefined} $color={cLabel}>
|
|
2256
2361
|
{rowChromeIcon}
|
|
2257
2362
|
{/* label + asterisk + help flow as ONE inline run, so the asterisk
|
|
2258
2363
|
stays right after the last word even when the name wraps. */}
|
|
@@ -2322,8 +2427,15 @@ export const CompactRow = memo(
|
|
|
2322
2427
|
// actual data — crucial when stacked under a (muted) description on
|
|
2323
2428
|
// mobile, where a dim value blends into the prose. The bold name still
|
|
2324
2429
|
// outranks it; the empty dash stays faint.
|
|
2325
|
-
$color={
|
|
2326
|
-
|
|
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}
|
|
2327
2439
|
>
|
|
2328
2440
|
{/* A markdown value with a host renderer draws in full in the inset
|
|
2329
2441
|
below, so the row prints no value line at all: the stripped-prose
|
|
@@ -2358,13 +2470,16 @@ export const CompactRow = memo(
|
|
|
2358
2470
|
null
|
|
2359
2471
|
: <span className='options-readfirst-valuetext'>
|
|
2360
2472
|
{hidden || empty ?
|
|
2361
|
-
formattedDefault || '—'
|
|
2473
|
+
calmRequiredWord || formattedDefault || '—'
|
|
2362
2474
|
: renderReadFirstValue(optionField, schema, formatted, readOnly)}
|
|
2363
2475
|
</span>}
|
|
2364
2476
|
{readOnlyChoiceDesc ?
|
|
2365
2477
|
<span className='options-readfirst-choice-desc'>{readOnlyChoiceDesc}</span>
|
|
2366
2478
|
: null}
|
|
2367
|
-
{
|
|
2479
|
+
{(calmRequiredWord ?
|
|
2480
|
+
valueReasons.filter((m) => m.content !== calmRequiredWord)
|
|
2481
|
+
: valueReasons
|
|
2482
|
+
).map((m, i) => (
|
|
2368
2483
|
<span
|
|
2369
2484
|
key={`${m.content}-${i}`}
|
|
2370
2485
|
className='options-readfirst-reason'
|
|
@@ -2606,7 +2721,12 @@ export const CompactRow = memo(
|
|
|
2606
2721
|
}}
|
|
2607
2722
|
/>
|
|
2608
2723
|
: dotColor ?
|
|
2609
|
-
<StyledStatusDot
|
|
2724
|
+
<StyledStatusDot
|
|
2725
|
+
className='options-readfirst-status-dot'
|
|
2726
|
+
$color={dotColor}
|
|
2727
|
+
$ring={dotStatus !== 'set'}
|
|
2728
|
+
aria-hidden
|
|
2729
|
+
/>
|
|
2610
2730
|
: null}
|
|
2611
2731
|
</StyledActionSlot>
|
|
2612
2732
|
</StyledRowActions>
|
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
} from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
12
12
|
import { useReqoreTheme } from '@qoretechnologies/reqore/dist/hooks/useTheme';
|
|
13
13
|
import React, { memo } from 'react';
|
|
14
|
-
import styled from 'styled-components';
|
|
14
|
+
import styled, { css } from 'styled-components';
|
|
15
15
|
import { useContext } from 'use-context-selector';
|
|
16
16
|
import { CompactToolbarContext, TCompactSort } from './compactToolbarContext';
|
|
17
|
+
import { useIsCalmForm } from './formAppearance';
|
|
17
18
|
|
|
18
19
|
// Field sort modes, listed in the Fields menu's "Sort by" submenu. 'schema' is
|
|
19
20
|
// the default (the schema's declared order); the rest reorder within each group.
|
|
@@ -28,11 +29,21 @@ const SORT_MODES: { value: TCompactSort; label: string; tooltip: string }[] = [
|
|
|
28
29
|
// "Focus" header: a summary line ({pct}% complete · {set}/{total} set ·
|
|
29
30
|
// {attention} need attention →) above a SEGMENTED meter — a green run (set) then
|
|
30
31
|
// an amber run (needs attention) then the empty remainder.
|
|
31
|
-
const StyledCompletion = styled.div
|
|
32
|
+
const StyledCompletion = styled.div<{ $calm?: boolean }>`
|
|
32
33
|
display: flex;
|
|
33
34
|
flex-flow: column;
|
|
34
35
|
gap: 8px;
|
|
35
36
|
padding: 0 2px;
|
|
37
|
+
/* Calm: one line — the summary, then a short meter beside it — that wraps
|
|
38
|
+
the meter under the words where there is no room for both. */
|
|
39
|
+
${({ $calm }) =>
|
|
40
|
+
$calm ?
|
|
41
|
+
css`
|
|
42
|
+
flex-flow: row wrap;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 6px 14px;
|
|
45
|
+
`
|
|
46
|
+
: ''}
|
|
36
47
|
`;
|
|
37
48
|
const StyledCompletionLine = styled.div`
|
|
38
49
|
display: flex;
|
|
@@ -46,11 +57,20 @@ const StyledMeter = styled.div<{
|
|
|
46
57
|
$track: string;
|
|
47
58
|
$set_c: string;
|
|
48
59
|
$att_c: string;
|
|
60
|
+
/** Calm: a short meter beside the summary instead of a full-width bar. */
|
|
61
|
+
$calm?: boolean;
|
|
49
62
|
}>`
|
|
50
63
|
position: relative;
|
|
51
64
|
height: 4px;
|
|
52
|
-
border-radius:
|
|
65
|
+
border-radius: ${({ $calm }) => ($calm ? 2 : 3)}px;
|
|
53
66
|
width: 100%;
|
|
67
|
+
${({ $calm }) =>
|
|
68
|
+
$calm ?
|
|
69
|
+
css`
|
|
70
|
+
flex: 1 1 160px;
|
|
71
|
+
max-width: 360px;
|
|
72
|
+
`
|
|
73
|
+
: ''}
|
|
54
74
|
overflow: hidden;
|
|
55
75
|
background: ${({ $track }) => $track};
|
|
56
76
|
/* No per-segment radius — the container's overflow:hidden + radius rounds only
|
|
@@ -113,7 +133,9 @@ export const CompactToolbar = memo((reqoreProps: Partial<IReqoreControlGroupProp
|
|
|
113
133
|
readOnly,
|
|
114
134
|
invalidCount,
|
|
115
135
|
attentionCount,
|
|
136
|
+
attentionFaultCount,
|
|
116
137
|
completion,
|
|
138
|
+
showInvalidOnly,
|
|
117
139
|
onToggleInvalidOnly,
|
|
118
140
|
hasMultipleOptions,
|
|
119
141
|
compactQuery,
|
|
@@ -135,16 +157,40 @@ export const CompactToolbar = memo((reqoreProps: Partial<IReqoreControlGroupProp
|
|
|
135
157
|
} = useContext(CompactToolbarContext);
|
|
136
158
|
|
|
137
159
|
const theme = useReqoreTheme();
|
|
160
|
+
const calm = useIsCalmForm();
|
|
138
161
|
const intents = (theme.intents || {}) as Record<string, string>;
|
|
162
|
+
/* "N need attention →" / "N to fill in →" is a toggle (show only those fields), so it
|
|
163
|
+
is one: a button a keyboard reaches, which says whether it is on. A host that turns
|
|
164
|
+
it on for a person (a blocked submit) reads `aria-pressed` rather than guessing,
|
|
165
|
+
since the toolbar draws a different link once something becomes a fault. */
|
|
166
|
+
const attentionToggleProps = {
|
|
167
|
+
className: 'options-readfirst-attention-link',
|
|
168
|
+
role: 'button',
|
|
169
|
+
tabIndex: 0,
|
|
170
|
+
'aria-pressed': !!showInvalidOnly,
|
|
171
|
+
onClick: onToggleInvalidOnly,
|
|
172
|
+
onKeyDown: (event: React.KeyboardEvent) => {
|
|
173
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
onToggleInvalidOnly?.();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
};
|
|
139
179
|
const cSuccess = intents.success || '#4a7110';
|
|
140
180
|
const cWarning = intents.warning || '#d17c29';
|
|
141
181
|
const cText = (theme.text?.color as string) || '#e8e8e8';
|
|
142
182
|
const cTrack = `${cText}1f`;
|
|
183
|
+
// Calm: what is done is grey — finished work needs nobody — and only the
|
|
184
|
+
// outstanding part (the amber run, the "need attention" link) is coloured.
|
|
185
|
+
const cDone = calm ? `${cText}8c` : cSuccess;
|
|
186
|
+
const cQuiet = `${cText}a6`;
|
|
143
187
|
// The GREEN run is what is done, not merely what has a value: a field that is
|
|
144
188
|
// set but flagged belongs to the amber run, and counting it twice pushed that
|
|
145
189
|
// run off the end of the track.
|
|
146
190
|
const setPct = completion.total ? (completion.done / completion.total) * 100 : 0;
|
|
147
|
-
|
|
191
|
+
// The amber run is what is WRONG. On a new form the required fields are only still
|
|
192
|
+
// to fill in, so the track shows them as not done yet, without amber.
|
|
193
|
+
const attentionPct = completion.total ? (attentionFaultCount / completion.total) * 100 : 0;
|
|
148
194
|
|
|
149
195
|
return (
|
|
150
196
|
<ReqoreControlGroup
|
|
@@ -161,11 +207,19 @@ export const CompactToolbar = memo((reqoreProps: Partial<IReqoreControlGroupProp
|
|
|
161
207
|
work. The read view lists what is set, and that is the whole
|
|
162
208
|
answer. */}
|
|
163
209
|
{parts.completion && completion.total && !readOnly ?
|
|
164
|
-
<StyledCompletion className='options-readfirst-completion'>
|
|
210
|
+
<StyledCompletion className='options-readfirst-completion' $calm={calm}>
|
|
165
211
|
<StyledCompletionLine>
|
|
166
212
|
<StyledSummary
|
|
167
213
|
className='options-readfirst-status'
|
|
168
|
-
|
|
214
|
+
// Calm: the word in the text colour. "Ready" is not a healthy
|
|
215
|
+
// figure to celebrate in green, and "Incomplete" is said in amber
|
|
216
|
+
// by the link beside it — only the outstanding part is coloured.
|
|
217
|
+
$color={
|
|
218
|
+
calm ? cText
|
|
219
|
+
: invalidCount || attentionFaultCount ? cWarning
|
|
220
|
+
: attentionCount ? cText
|
|
221
|
+
: cSuccess
|
|
222
|
+
}
|
|
169
223
|
style={{ fontWeight: 600 }}
|
|
170
224
|
>
|
|
171
225
|
{/* "Ready" is a claim about the whole form, so anything the form
|
|
@@ -179,28 +233,43 @@ export const CompactToolbar = memo((reqoreProps: Partial<IReqoreControlGroupProp
|
|
|
179
233
|
batched mode's per-row chip alone. */}
|
|
180
234
|
{invalidCount || attentionCount ? 'Incomplete' : 'Ready'}
|
|
181
235
|
</StyledSummary>
|
|
182
|
-
<StyledSummary style={{ opacity: 0.5 }}>
|
|
236
|
+
<StyledSummary style={calm ? { color: cQuiet } : { opacity: 0.5 }}>
|
|
183
237
|
· {completion.set}/{completion.total} set
|
|
184
238
|
{attentionCount ? ' ·' : ''}
|
|
185
239
|
</StyledSummary>
|
|
186
|
-
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{attentionCount} need attention →
|
|
240
|
+
{/* Amber only for what is a fault. A new form's required fields are still to
|
|
241
|
+
fill in, which is the next step, not a warning: they are counted apart,
|
|
242
|
+
in the quiet colour. */}
|
|
243
|
+
{attentionFaultCount ?
|
|
244
|
+
<StyledAttentionLink $color={cWarning} {...attentionToggleProps}>
|
|
245
|
+
{attentionFaultCount} need attention →
|
|
193
246
|
</StyledAttentionLink>
|
|
194
247
|
: null}
|
|
195
|
-
|
|
248
|
+
{attentionCount > attentionFaultCount ?
|
|
249
|
+
attentionFaultCount ?
|
|
250
|
+
<StyledSummary style={{ color: cQuiet }}>
|
|
251
|
+
· {attentionCount - attentionFaultCount} to fill in
|
|
252
|
+
</StyledSummary>
|
|
253
|
+
: <StyledAttentionLink $color={cQuiet} {...attentionToggleProps}>
|
|
254
|
+
{attentionCount} to fill in →
|
|
255
|
+
</StyledAttentionLink>
|
|
256
|
+
: null}
|
|
257
|
+
{/* Calm: the percentage is one more quiet figure on the line, not
|
|
258
|
+
a large bold number pinned to the far edge. */}
|
|
259
|
+
{calm ?
|
|
260
|
+
<StyledSummary className='options-readfirst-pct' style={{ color: cQuiet }}>
|
|
261
|
+
· {completion.pct}%
|
|
262
|
+
</StyledSummary>
|
|
263
|
+
: <StyledPct className='options-readfirst-pct'>{completion.pct}%</StyledPct>}
|
|
196
264
|
</StyledCompletionLine>
|
|
197
265
|
<StyledMeter
|
|
198
266
|
className='options-readfirst-completion-bar'
|
|
199
267
|
$set={setPct}
|
|
200
268
|
$attention={attentionPct}
|
|
201
269
|
$track={cTrack}
|
|
202
|
-
$set_c={
|
|
270
|
+
$set_c={cDone}
|
|
203
271
|
$att_c={cWarning}
|
|
272
|
+
$calm={calm}
|
|
204
273
|
/>
|
|
205
274
|
</StyledCompletion>
|
|
206
275
|
: null}
|