@qoretechnologies/reqraft 0.10.65 → 0.10.66-pr.145.g2d1b4c8
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 +128 -24
- 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 +243 -85
- 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/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 +5 -3
- 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 +4 -2
- package/dist/components/form/fields/allowed-values/AllowedValues.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/index.d.ts +1 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +1 -0
- package/dist/components/form/index.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/ticketThread/TicketThread.d.ts.map +1 -1
- package/dist/components/ticketThread/TicketThread.js +8 -16
- package/dist/components/ticketThread/TicketThread.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/engine/CompactRow.tsx +141 -22
- 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 +358 -77
- 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/fields/ChoiceGroupHeading.tsx +38 -0
- package/src/components/form/fields/Field.tsx +6 -5
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +7 -4
- package/src/components/form/fields/radio-group/RadioGroup.tsx +13 -7
- package/src/components/form/index.tsx +1 -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/ticketThread/TicketThread.stories.tsx +31 -2
- package/src/components/ticketThread/TicketThread.tsx +13 -28
|
@@ -35,6 +35,7 @@ import { Description } from '../../Description';
|
|
|
35
35
|
import { useMarkdownRenderer } from '../../Description/markdownRendererContext';
|
|
36
36
|
import { FocusedEditing } from '../../FocusedEditing';
|
|
37
37
|
import { CompactRowContext } from './compactRowContext';
|
|
38
|
+
import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL, useIsCalmForm } from './formAppearance';
|
|
38
39
|
import { revealRow } from './revealRow';
|
|
39
40
|
import {
|
|
40
41
|
StyledActionSlot,
|
|
@@ -192,8 +193,17 @@ export const CompactRow = memo(
|
|
|
192
193
|
clusterLast?: boolean;
|
|
193
194
|
}) => {
|
|
194
195
|
const readOnly = useContextSelector(CompactRowContext, (v) => v.readOnly);
|
|
196
|
+
// The look only — see `TFormEngineAppearance`. Nothing a row DOES reads it.
|
|
197
|
+
const calm = useIsCalmForm();
|
|
195
198
|
const commitMode = useContextSelector(CompactRowContext, (v) => v.commitMode);
|
|
196
199
|
const options = useContextSelector(CompactRowContext, (v) => v.options);
|
|
200
|
+
// Whether this field's emptiness is a fault yet: the person has opened it and left,
|
|
201
|
+
// or the host reported a submit attempt. Before that a required field is only to
|
|
202
|
+
// fill in, and says so in the quiet colour.
|
|
203
|
+
const visited = useContextSelector(
|
|
204
|
+
CompactRowContext,
|
|
205
|
+
(v) => v.revealRequired || !!v.visitedOptions?.includes(optionName)
|
|
206
|
+
);
|
|
197
207
|
|
|
198
208
|
// An absorbed field keeps its own name — it is a different field, not a
|
|
199
209
|
// property of its host — so the label comes from its schema exactly as it
|
|
@@ -296,6 +306,11 @@ export const CompactRow = memo(
|
|
|
296
306
|
const cDanger = useContextSelector(CompactRowContext, (v) => v.cDanger);
|
|
297
307
|
const cWarning = useContextSelector(CompactRowContext, (v) => v.cWarning);
|
|
298
308
|
const cInfo = useContextSelector(CompactRowContext, (v) => v.cInfo);
|
|
309
|
+
const cText = useContextSelector(CompactRowContext, (v) => v.cText);
|
|
310
|
+
// A field's NAME: full text colour by default; grey in the calm look, so the
|
|
311
|
+
// value beside it is what the eye lands on. One colour for the read row, the
|
|
312
|
+
// open row and the edit card, so a name does not change as its field opens.
|
|
313
|
+
const cLabel = calm ? `${cText}b3` : cKey;
|
|
299
314
|
// A markdown value renders as a document in the row's inset; the host's
|
|
300
315
|
// renderer when it supplied one, reqraft's own otherwise.
|
|
301
316
|
|
|
@@ -402,6 +417,11 @@ export const CompactRow = memo(
|
|
|
402
417
|
}
|
|
403
418
|
|
|
404
419
|
if (valueType === 'bool' || valueType === 'boolean') {
|
|
420
|
+
// Calm: Yes and No are words. Neither is a status — "No" is not a
|
|
421
|
+
// failure — so neither is coloured.
|
|
422
|
+
if (calm) {
|
|
423
|
+
return <span style={textStyle}>{formatted}</span>;
|
|
424
|
+
}
|
|
405
425
|
const truthy = field?.value === true || field?.value === 'true';
|
|
406
426
|
return (
|
|
407
427
|
<ReqoreTag
|
|
@@ -701,8 +721,11 @@ export const CompactRow = memo(
|
|
|
701
721
|
);
|
|
702
722
|
};
|
|
703
723
|
const required = !!(schema?.required || schema?.required_groups);
|
|
704
|
-
// The asterisk says "you have to fill this in". Read-only, nobody does.
|
|
705
|
-
|
|
724
|
+
// The asterisk says "you have to fill this in". Read-only, nobody does. The
|
|
725
|
+
// calm look draws none at all: an empty required field says so in its
|
|
726
|
+
// value column instead ("Required · add a value"), and a filled one has
|
|
727
|
+
// nothing left to ask for.
|
|
728
|
+
const requiredMark = required && !readOnly && !calm;
|
|
706
729
|
const removable =
|
|
707
730
|
!readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
|
|
708
731
|
const changed = !hidden && !readOnly && hasOptionChanged(optionField?.value, optionName);
|
|
@@ -767,8 +790,15 @@ export const CompactRow = memo(
|
|
|
767
790
|
minimal
|
|
768
791
|
flat
|
|
769
792
|
compact
|
|
770
|
-
effect={{ uppercase: true, spaced: 1 }}
|
|
771
|
-
|
|
793
|
+
effect={calm ? undefined : { uppercase: true, spaced: 1 }}
|
|
794
|
+
// Calm: a met group is nothing to point at — only the unmet one is
|
|
795
|
+
// coloured.
|
|
796
|
+
intent={
|
|
797
|
+
groupResolved ?
|
|
798
|
+
calm ? undefined
|
|
799
|
+
: 'success'
|
|
800
|
+
: 'warning'
|
|
801
|
+
}
|
|
772
802
|
icon={groupResolved ? 'CheckLine' : 'LinkM'}
|
|
773
803
|
label={
|
|
774
804
|
!groupResolved ? 'One of'
|
|
@@ -921,16 +951,39 @@ export const CompactRow = memo(
|
|
|
921
951
|
const editor = editorRef.current;
|
|
922
952
|
if (!editor) return undefined;
|
|
923
953
|
let settled = false;
|
|
954
|
+
/* A control that is not a text box holds the caret PROVISIONALLY. An editor that
|
|
955
|
+
takes text can mount after the row opens (a code editor loads late), and until
|
|
956
|
+
it does the only control there is its chrome: a fullscreen toggle. When the
|
|
957
|
+
text box arrives it takes the caret, unless the reader has moved it since. A
|
|
958
|
+
row that never grows one (a pick-one) simply keeps its control. */
|
|
959
|
+
let provisional: HTMLElement | null = null;
|
|
924
960
|
const takeCaret = (): boolean => {
|
|
925
961
|
if (settled) return true;
|
|
926
962
|
const active = document.activeElement as HTMLElement | null;
|
|
963
|
+
if (provisional) {
|
|
964
|
+
if (active !== provisional) {
|
|
965
|
+
// The reader moved the caret themselves. Leave it there.
|
|
966
|
+
settled = true;
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
const textEntry = findRowFocusTarget(editor, { textEntryOnly: true });
|
|
970
|
+
if (!textEntry) return false;
|
|
971
|
+
textEntry.focus({ preventScroll: true });
|
|
972
|
+
// An editor's text box can be in the DOM before it can take focus (Monaco's
|
|
973
|
+
// edit area, mid-layout), and `focus()` then does nothing. Only a caret
|
|
974
|
+
// that landed counts; otherwise try again on the editor's next change.
|
|
975
|
+
if (document.activeElement !== textEntry) return false;
|
|
976
|
+
settled = true;
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
927
979
|
if (active && active !== document.body && !rowRef.current?.contains(active)) {
|
|
928
980
|
// Someone else holds the caret — a field the reader moved into while
|
|
929
981
|
// this row's editor was still arriving. Leave it where it is.
|
|
930
982
|
settled = true;
|
|
931
983
|
return true;
|
|
932
984
|
}
|
|
933
|
-
|
|
985
|
+
// A card hands the caret only to where text goes (see `findRowFocusTarget`).
|
|
986
|
+
const control = findRowFocusTarget(editor, { textEntryOnly: !inlineEditable });
|
|
934
987
|
if (!control) return false;
|
|
935
988
|
// The least scroll that shows the row clear of the pinned box header,
|
|
936
989
|
// and none when it is already in view (see `revealRow`).
|
|
@@ -940,6 +993,9 @@ export const CompactRow = memo(
|
|
|
940
993
|
// `preventScroll`, because the reveal above has already put the row
|
|
941
994
|
// where it belongs; letting focus scroll as well moves it twice.
|
|
942
995
|
control.focus({ preventScroll: true });
|
|
996
|
+
// A control that cannot take focus yet (still laying out) did not take
|
|
997
|
+
// the caret: try again on the editor's next change.
|
|
998
|
+
if (document.activeElement !== control) return false;
|
|
943
999
|
/* …and if the control the row offers is a PICKER, open it.
|
|
944
1000
|
*
|
|
945
1001
|
* A closed picker prints the value it holds, which is exactly what the
|
|
@@ -957,6 +1013,13 @@ export const CompactRow = memo(
|
|
|
957
1013
|
if (openedByAuthor.current && inlineEditable && isClosedPickerTrigger(control)) {
|
|
958
1014
|
openedByAuthor.current = false;
|
|
959
1015
|
setOpenThePicker(true);
|
|
1016
|
+
settled = true;
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
if (findRowFocusTarget(editor, { textEntryOnly: true }) !== control) {
|
|
1020
|
+
// Not a text box: hold it until one arrives (see `provisional`).
|
|
1021
|
+
provisional = control;
|
|
1022
|
+
return false;
|
|
960
1023
|
}
|
|
961
1024
|
settled = true;
|
|
962
1025
|
return true;
|
|
@@ -965,7 +1028,9 @@ export const CompactRow = memo(
|
|
|
965
1028
|
const observer = new MutationObserver(() => {
|
|
966
1029
|
if (takeCaret()) observer.disconnect();
|
|
967
1030
|
});
|
|
968
|
-
|
|
1031
|
+
// Attributes too: an editor's text box can become focusable by an attribute or
|
|
1032
|
+
// style change after it is inserted (see the `focus()` checks above).
|
|
1033
|
+
observer.observe(editor, { childList: true, subtree: true, attributes: true });
|
|
969
1034
|
return () => observer.disconnect();
|
|
970
1035
|
}, [isExpanded, optionName, inlineEditable]);
|
|
971
1036
|
|
|
@@ -1150,7 +1215,7 @@ export const CompactRow = memo(
|
|
|
1150
1215
|
fixed
|
|
1151
1216
|
flat
|
|
1152
1217
|
compact
|
|
1153
|
-
effect={{ uppercase: true, spaced: 1 }}
|
|
1218
|
+
effect={calm ? undefined : { uppercase: true, spaced: 1 }}
|
|
1154
1219
|
/>
|
|
1155
1220
|
: null;
|
|
1156
1221
|
const isPassiveCloseAction = fixedAllowedValueOption;
|
|
@@ -1174,7 +1239,7 @@ export const CompactRow = memo(
|
|
|
1174
1239
|
minimal
|
|
1175
1240
|
fixed
|
|
1176
1241
|
icon='CloseLine'
|
|
1177
|
-
intent='warning'
|
|
1242
|
+
intent={calm ? undefined : 'warning'}
|
|
1178
1243
|
// "Cancel edit", not "Cancel": a bare Cancel collides with the one
|
|
1179
1244
|
// in Reqore's confirmation modal, and this one is specifically about
|
|
1180
1245
|
// abandoning the edit rather than dismissing a dialog.
|
|
@@ -1195,7 +1260,9 @@ export const CompactRow = memo(
|
|
|
1195
1260
|
<ReqoreButton
|
|
1196
1261
|
className='options-readfirst-done'
|
|
1197
1262
|
size='small'
|
|
1198
|
-
|
|
1263
|
+
// Calm: a neutral button. Closing a field is not a healthy figure and
|
|
1264
|
+
// not the page's one primary action, so it takes neither colour.
|
|
1265
|
+
intent={!isPassiveCloseAction && !calm ? 'success' : undefined}
|
|
1199
1266
|
flat={isPassiveCloseAction}
|
|
1200
1267
|
minimal={isPassiveCloseAction}
|
|
1201
1268
|
fixed
|
|
@@ -1236,6 +1303,10 @@ export const CompactRow = memo(
|
|
|
1236
1303
|
allOptions: availableOptions,
|
|
1237
1304
|
getType: getTypeForOption,
|
|
1238
1305
|
isRendererOnly,
|
|
1306
|
+
calm,
|
|
1307
|
+
readOnly,
|
|
1308
|
+
// Until the person has been to the field, its requirement is stated plainly.
|
|
1309
|
+
requirementPending: !visited,
|
|
1239
1310
|
}).map((m) => ({ intent: m.intent as string, content: String(m.label) }))
|
|
1240
1311
|
: [];
|
|
1241
1312
|
// TWO display channels, matching the Focus prototype:
|
|
@@ -1317,7 +1388,14 @@ export const CompactRow = memo(
|
|
|
1317
1388
|
size='small'
|
|
1318
1389
|
opaque={false}
|
|
1319
1390
|
flat
|
|
1320
|
-
|
|
1391
|
+
// Calm: a note that only informs takes no colour — blue is kept for "a
|
|
1392
|
+
// filter is on", green for a healthy figure. A warning or a failure
|
|
1393
|
+
// keeps its intent: those are what a person has to act on.
|
|
1394
|
+
intent={
|
|
1395
|
+
(calm && (!m.intent || m.intent === 'info' || m.intent === 'success') ?
|
|
1396
|
+
undefined
|
|
1397
|
+
: m.intent) as never
|
|
1398
|
+
}
|
|
1321
1399
|
title={m.title}
|
|
1322
1400
|
style={{ marginBottom: 8 }}
|
|
1323
1401
|
>
|
|
@@ -1493,7 +1571,7 @@ export const CompactRow = memo(
|
|
|
1493
1571
|
tabIndex={0}
|
|
1494
1572
|
aria-label={`Collapse ${label}`}
|
|
1495
1573
|
title={schema?.short_desc || undefined}
|
|
1496
|
-
$color={
|
|
1574
|
+
$color={cLabel}
|
|
1497
1575
|
$pointer
|
|
1498
1576
|
onClick={collapse}
|
|
1499
1577
|
onKeyDown={(event) => {
|
|
@@ -1504,7 +1582,7 @@ export const CompactRow = memo(
|
|
|
1504
1582
|
}}
|
|
1505
1583
|
>
|
|
1506
1584
|
{label}
|
|
1507
|
-
{required ?
|
|
1585
|
+
{required && !calm ?
|
|
1508
1586
|
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1509
1587
|
: null}
|
|
1510
1588
|
{helpIcon}
|
|
@@ -1526,6 +1604,7 @@ export const CompactRow = memo(
|
|
|
1526
1604
|
</StyledLabelBlock>
|
|
1527
1605
|
<div
|
|
1528
1606
|
ref={editorRef}
|
|
1607
|
+
className='options-readfirst-row-editor'
|
|
1529
1608
|
style={{ minWidth: 0 }}
|
|
1530
1609
|
onKeyDown={(event) => {
|
|
1531
1610
|
if (event.key === 'Escape') {
|
|
@@ -1664,6 +1743,7 @@ export const CompactRow = memo(
|
|
|
1664
1743
|
return (
|
|
1665
1744
|
<StyledEditCard
|
|
1666
1745
|
key={optionName}
|
|
1746
|
+
ref={rowRef}
|
|
1667
1747
|
data-field={optionName}
|
|
1668
1748
|
className={
|
|
1669
1749
|
COMPACT_SINGLE_VALUE_TYPES.has(editType) ?
|
|
@@ -1672,6 +1752,7 @@ export const CompactRow = memo(
|
|
|
1672
1752
|
}
|
|
1673
1753
|
$bg={cHover}
|
|
1674
1754
|
$border={schemaIntentColor ? `${schemaIntentColor}66` : `${cInfo}66`}
|
|
1755
|
+
$calm={calm}
|
|
1675
1756
|
>
|
|
1676
1757
|
<div
|
|
1677
1758
|
style={{
|
|
@@ -1682,7 +1763,7 @@ export const CompactRow = memo(
|
|
|
1682
1763
|
}}
|
|
1683
1764
|
>
|
|
1684
1765
|
<StyledCardHeading>
|
|
1685
|
-
<StyledCardLabel $color={
|
|
1766
|
+
<StyledCardLabel $color={cLabel}>
|
|
1686
1767
|
{(
|
|
1687
1768
|
(schema as { icon?: string } | undefined)?.icon ||
|
|
1688
1769
|
(schema as { image?: string } | undefined)?.image
|
|
@@ -1695,7 +1776,7 @@ export const CompactRow = memo(
|
|
|
1695
1776
|
: null}
|
|
1696
1777
|
<span>
|
|
1697
1778
|
{label}
|
|
1698
|
-
{required ?
|
|
1779
|
+
{required && !calm ?
|
|
1699
1780
|
<ReqoreIcon icon='Asterisk' color='danger' size='10px' />
|
|
1700
1781
|
: null}
|
|
1701
1782
|
{/* The `?` belongs here too. The read row and the inline
|
|
@@ -1802,6 +1883,11 @@ export const CompactRow = memo(
|
|
|
1802
1883
|
keystroke a silent commit. Same handler, same `cancelEdit()`, so
|
|
1803
1884
|
the two shapes cannot diverge. */}
|
|
1804
1885
|
<div
|
|
1886
|
+
// The card's editor, as the inline row's is: an opened card takes the
|
|
1887
|
+
// caret too (the tap-to-edit rule above), so opening Description or
|
|
1888
|
+
// Source Code by a click or by a blocked submit puts the cursor in it.
|
|
1889
|
+
ref={editorRef}
|
|
1890
|
+
className='options-readfirst-row-editor'
|
|
1805
1891
|
style={{ display: 'contents' }}
|
|
1806
1892
|
onKeyDown={(event) => {
|
|
1807
1893
|
if (event.key === 'Escape') {
|
|
@@ -1861,7 +1947,7 @@ export const CompactRow = memo(
|
|
|
1861
1947
|
const valueReasons: TInfoMsg[] = [
|
|
1862
1948
|
...inlineMessages,
|
|
1863
1949
|
...(empty && coveredByLabel ?
|
|
1864
|
-
[{ content: `Covered by “${coveredByLabel}”`, intent: 'success' }]
|
|
1950
|
+
[{ content: `Covered by “${coveredByLabel}”`, intent: calm ? undefined : 'success' }]
|
|
1865
1951
|
: []),
|
|
1866
1952
|
];
|
|
1867
1953
|
// A hash row reveals its sub-fields as read-only sub-rows under a "view
|
|
@@ -2207,10 +2293,28 @@ export const CompactRow = memo(
|
|
|
2207
2293
|
covered: !!coveredByLabel || groupResolved,
|
|
2208
2294
|
invalid: worstIntent === 'danger',
|
|
2209
2295
|
warned: worstIntent === 'warning',
|
|
2296
|
+
visited,
|
|
2210
2297
|
});
|
|
2211
2298
|
// No dot at all in a read-only form: the dot reports progress on the
|
|
2212
2299
|
// form, and a read view has no progress to report.
|
|
2213
|
-
|
|
2300
|
+
//
|
|
2301
|
+
// Calm: no dot on a healthy row, and none on an empty required one either —
|
|
2302
|
+
// its value column already says "Required · add a value" in amber, and one
|
|
2303
|
+
// thing is said once. Only a failing value keeps the red dot beside the
|
|
2304
|
+
// reason it gives.
|
|
2305
|
+
const dotStatus =
|
|
2306
|
+
hidden || fieldDisabled || readOnly || (calm && rowStatus !== 'invalid') ?
|
|
2307
|
+
undefined
|
|
2308
|
+
: rowStatus;
|
|
2309
|
+
// Calm: an empty required value is said IN the value column, in amber,
|
|
2310
|
+
// where the value would be — instead of a dash plus a red reason beside it.
|
|
2311
|
+
// Not on a locked field (its lock and reason speak for it) or one that
|
|
2312
|
+
// shows the default it will fall back to.
|
|
2313
|
+
const calmRequiredWord =
|
|
2314
|
+
calm && empty && !hidden && !!schema?.required && !fieldDisabled && !formattedDefault ?
|
|
2315
|
+
readOnly ? CALM_REQUIRED_READ_LABEL
|
|
2316
|
+
: CALM_REQUIRED_LABEL
|
|
2317
|
+
: undefined;
|
|
2214
2318
|
const dotColor =
|
|
2215
2319
|
dotStatus === 'invalid' ? cDanger
|
|
2216
2320
|
: dotStatus === 'todo' ? cWarning
|
|
@@ -2252,7 +2356,7 @@ export const CompactRow = memo(
|
|
|
2252
2356
|
>
|
|
2253
2357
|
{clusterNode}
|
|
2254
2358
|
<StyledLabelBlock className='options-readfirst-label-block'>
|
|
2255
|
-
<StyledRowLabel title={schema?.short_desc || undefined} $color={
|
|
2359
|
+
<StyledRowLabel title={schema?.short_desc || undefined} $color={cLabel}>
|
|
2256
2360
|
{rowChromeIcon}
|
|
2257
2361
|
{/* label + asterisk + help flow as ONE inline run, so the asterisk
|
|
2258
2362
|
stays right after the last word even when the name wraps. */}
|
|
@@ -2322,8 +2426,15 @@ export const CompactRow = memo(
|
|
|
2322
2426
|
// actual data — crucial when stacked under a (muted) description on
|
|
2323
2427
|
// mobile, where a dim value blends into the prose. The bold name still
|
|
2324
2428
|
// outranks it; the empty dash stays faint.
|
|
2325
|
-
$color={
|
|
2326
|
-
|
|
2429
|
+
$color={
|
|
2430
|
+
calmRequiredWord ?
|
|
2431
|
+
rowStatus === 'pending' ?
|
|
2432
|
+
`${cMuted}a6`
|
|
2433
|
+
: cWarning
|
|
2434
|
+
: empty || hidden ? `${cMuted}66`
|
|
2435
|
+
: cKey
|
|
2436
|
+
}
|
|
2437
|
+
$empty={(empty || hidden) && !calmRequiredWord}
|
|
2327
2438
|
>
|
|
2328
2439
|
{/* A markdown value with a host renderer draws in full in the inset
|
|
2329
2440
|
below, so the row prints no value line at all: the stripped-prose
|
|
@@ -2358,13 +2469,16 @@ export const CompactRow = memo(
|
|
|
2358
2469
|
null
|
|
2359
2470
|
: <span className='options-readfirst-valuetext'>
|
|
2360
2471
|
{hidden || empty ?
|
|
2361
|
-
formattedDefault || '—'
|
|
2472
|
+
calmRequiredWord || formattedDefault || '—'
|
|
2362
2473
|
: renderReadFirstValue(optionField, schema, formatted, readOnly)}
|
|
2363
2474
|
</span>}
|
|
2364
2475
|
{readOnlyChoiceDesc ?
|
|
2365
2476
|
<span className='options-readfirst-choice-desc'>{readOnlyChoiceDesc}</span>
|
|
2366
2477
|
: null}
|
|
2367
|
-
{
|
|
2478
|
+
{(calmRequiredWord ?
|
|
2479
|
+
valueReasons.filter((m) => m.content !== calmRequiredWord)
|
|
2480
|
+
: valueReasons
|
|
2481
|
+
).map((m, i) => (
|
|
2368
2482
|
<span
|
|
2369
2483
|
key={`${m.content}-${i}`}
|
|
2370
2484
|
className='options-readfirst-reason'
|
|
@@ -2606,7 +2720,12 @@ export const CompactRow = memo(
|
|
|
2606
2720
|
}}
|
|
2607
2721
|
/>
|
|
2608
2722
|
: dotColor ?
|
|
2609
|
-
<StyledStatusDot
|
|
2723
|
+
<StyledStatusDot
|
|
2724
|
+
className='options-readfirst-status-dot'
|
|
2725
|
+
$color={dotColor}
|
|
2726
|
+
$ring={dotStatus !== 'set'}
|
|
2727
|
+
aria-hidden
|
|
2728
|
+
/>
|
|
2610
2729
|
: null}
|
|
2611
2730
|
</StyledActionSlot>
|
|
2612
2731
|
</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}
|