@qoretechnologies/reqraft 0.10.16 → 0.10.17

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.
Files changed (53) hide show
  1. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  2. package/dist/components/form/engine/CompactRow.js +82 -3
  3. package/dist/components/form/engine/CompactRow.js.map +1 -1
  4. package/dist/components/form/engine/FormEngine.d.ts +37 -11
  5. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  6. package/dist/components/form/engine/FormEngine.js +174 -110
  7. package/dist/components/form/engine/FormEngine.js.map +1 -1
  8. package/dist/components/form/engine/compactRowContext.d.ts +8 -0
  9. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  10. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  11. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  12. package/dist/components/form/engine/compactRowStyles.js +1 -1
  13. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  14. package/dist/components/form/engine/optionActions.d.ts +28 -0
  15. package/dist/components/form/engine/optionActions.d.ts.map +1 -0
  16. package/dist/components/form/engine/optionActions.js +18 -0
  17. package/dist/components/form/engine/optionActions.js.map +1 -0
  18. package/dist/components/form/engine/readFirst.d.ts +9 -2
  19. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  20. package/dist/components/form/engine/readFirst.js +15 -3
  21. package/dist/components/form/engine/readFirst.js.map +1 -1
  22. package/dist/components/form/engine/rendererTypes.d.ts +35 -0
  23. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
  24. package/dist/components/form/engine/rendererTypes.js +63 -0
  25. package/dist/components/form/engine/rendererTypes.js.map +1 -0
  26. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  27. package/dist/components/form/fields/auto/AutoFormField.js +6 -1
  28. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  29. package/dist/components/form/index.d.ts +2 -0
  30. package/dist/components/form/index.d.ts.map +1 -1
  31. package/dist/components/form/index.js +2 -0
  32. package/dist/components/form/index.js.map +1 -1
  33. package/dist/hooks/useMediaQuery.d.ts +24 -0
  34. package/dist/hooks/useMediaQuery.d.ts.map +1 -0
  35. package/dist/hooks/useMediaQuery.js +56 -0
  36. package/dist/hooks/useMediaQuery.js.map +1 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +7 -1
  40. package/dist/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/components/form/engine/CompactRow.tsx +139 -0
  43. package/src/components/form/engine/FormEngine.tsx +222 -70
  44. package/src/components/form/engine/FormEngineRemote.stories.tsx +329 -0
  45. package/src/components/form/engine/compactRowContext.ts +8 -0
  46. package/src/components/form/engine/compactRowStyles.ts +30 -0
  47. package/src/components/form/engine/optionActions.ts +40 -0
  48. package/src/components/form/engine/readFirst.ts +25 -6
  49. package/src/components/form/engine/rendererTypes.ts +55 -0
  50. package/src/components/form/fields/auto/AutoFormField.tsx +6 -1
  51. package/src/components/form/index.tsx +2 -0
  52. package/src/hooks/useMediaQuery.ts +61 -0
  53. package/src/index.tsx +7 -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,
@@ -107,6 +109,12 @@ const COMPACT_SINGLE_VALUE_TYPES = new Set([
107
109
  'method-name',
108
110
  ]);
109
111
 
112
+ // How many consumer-injected actions may sit inline in a row before the rest
113
+ // overflow into the row's menu. A row's action slot shares space with the
114
+ // value; an unbounded button strip would squeeze the value out, and the
115
+ // consumer controls how many actions it injects.
116
+ const MAX_INLINE_OPTION_ACTIONS = 2;
117
+
110
118
 
111
119
  // One read-first row: label | value | action collapsed; the real editor (the
112
120
  // classic renderOption) expanded. `hidden` = search-surfaced optional —
@@ -176,6 +184,11 @@ export const CompactRow = memo(
176
184
  );
177
185
  const getTypeForOption = useContextSelector(CompactRowContext, (v) => v.getTypeForOption);
178
186
  const confirmAction = useContextSelector(CompactRowContext, (v) => v.confirmAction);
187
+ const optionActions = useContextSelector(CompactRowContext, (v) => v.optionActions);
188
+ const collapseOptionActions = useContextSelector(
189
+ CompactRowContext,
190
+ (v) => v.collapseOptionActions
191
+ );
179
192
  const renderOption = useContextSelector(CompactRowContext, (v) => v.renderOption);
180
193
  const theme = useContextSelector(CompactRowContext, (v) => v.theme);
181
194
  const cMuted = useContextSelector(CompactRowContext, (v) => v.cMuted);
@@ -338,6 +351,97 @@ export const CompactRow = memo(
338
351
 
339
352
  const schema = options?.[optionName];
340
353
  const label = schema?.display_name || optionName;
354
+ const injectedOptionActions = React.useMemo<IReqorePanelAction[]>(
355
+ () =>
356
+ schema ?
357
+ resolveOptionActions(optionActions, {
358
+ name: optionName,
359
+ schema,
360
+ value: availableOptions?.[optionName],
361
+ }).filter((action) => action.show !== false)
362
+ : [],
363
+ [availableOptions?.[optionName], optionActions, optionName, schema]
364
+ );
365
+ // Which injected actions stay as inline buttons, and which move into the
366
+ // overflow menu. Everything collapses on touch / narrow viewports — a
367
+ // hover-gated button is unreachable without a hover — and anything past the
368
+ // inline cap overflows regardless, so a consumer injecting ten actions can
369
+ // never push the row's value out of view.
370
+ const [inlineOptionActions, menuOptionActions] = React.useMemo<
371
+ [IReqorePanelAction[], IReqorePanelAction[]]
372
+ >(
373
+ () =>
374
+ collapseOptionActions ?
375
+ [[], injectedOptionActions]
376
+ : [
377
+ injectedOptionActions.slice(0, MAX_INLINE_OPTION_ACTIONS),
378
+ injectedOptionActions.slice(MAX_INLINE_OPTION_ACTIONS),
379
+ ],
380
+ [collapseOptionActions, injectedOptionActions]
381
+ );
382
+ const injectedOptionActionMenuItems = React.useMemo<IReqoreDropdownItem[]>(
383
+ () =>
384
+ menuOptionActions.map(
385
+ (action, index) =>
386
+ ({
387
+ // A panel action labels itself with `label`, but an icon-only one
388
+ // (the IDE's AI-assist button) carries its name in the tooltip —
389
+ // a menu row has no hover affordance to fall back on.
390
+ label: action.label ?? action.tooltip ?? `Action ${index + 1}`,
391
+ icon: action.icon,
392
+ intent: action.intent,
393
+ disabled: action.disabled,
394
+ onClick: () => action.onClick?.(),
395
+ }) as IReqoreDropdownItem
396
+ ),
397
+ [menuOptionActions]
398
+ );
399
+ const renderInjectedOptionAction = (
400
+ action: IReqorePanelAction,
401
+ index: number,
402
+ size: 'tiny' | 'small' = 'small'
403
+ ) => {
404
+ // `show` drives visibility here instead of reaching the DOM. The classic
405
+ // path hands these to ReqorePanel, which honours `show: 'hover'` itself;
406
+ // the compact slots render plain buttons, so 'hover' becomes a CSS gate on
407
+ // the containing row/card (see compactRowStyles) — same config, same
408
+ // behaviour in both modes.
409
+ // `size` is destructured out and deliberately NOT honoured: the row owns
410
+ // its action strip's rhythm, and a consumer-supplied size made the
411
+ // injected button visibly smaller than the revert / more / confirm
412
+ // buttons beside it. The contextual `size` argument is the row's own
413
+ // (small when editing or in a card, tiny on a read row), so injected
414
+ // actions always match their neighbours.
415
+ const {
416
+ label: actionLabel,
417
+ onClick,
418
+ show,
419
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
420
+ size: _ignoredSize,
421
+ className,
422
+ ...actionProps
423
+ } = action;
424
+
425
+ return (
426
+ <ReqoreButton
427
+ key={`${optionName}-injected-action-${index}`}
428
+ size={size}
429
+ minimal
430
+ flat
431
+ fixed
432
+ {...actionProps}
433
+ className={`${className ? `${className} ` : ''}options-injected-action${
434
+ show === 'hover' ? ' options-injected-action-hover' : ''
435
+ }`}
436
+ onClick={(event: React.MouseEvent<HTMLElement>) => {
437
+ event.stopPropagation();
438
+ onClick?.();
439
+ }}
440
+ >
441
+ {actionLabel}
442
+ </ReqoreButton>
443
+ );
444
+ };
341
445
  const required = !!(schema?.required || schema?.required_groups);
342
446
  const removable =
343
447
  !readOnly && !schema?.preselected && !schema?.required && !schema?.required_groups;
@@ -675,6 +779,10 @@ export const CompactRow = memo(
675
779
  } as IReqoreDropdownItem,
676
780
  ]
677
781
  : []),
782
+ // Consumer-injected actions that did not fit inline (or collapsed
783
+ // wholesale on touch) land here — this row already has a menu, so they
784
+ // reuse it rather than adding a second one beside it.
785
+ ...injectedOptionActionMenuItems,
678
786
  ]}
679
787
  />
680
788
  );
@@ -748,6 +856,9 @@ export const CompactRow = memo(
748
856
  message strip is visible. */}
749
857
  {revertButton}
750
858
  {clearValueButton}
859
+ {inlineOptionActions.map((action, index) =>
860
+ renderInjectedOptionAction(action, index)
861
+ )}
751
862
  {moreMenu}
752
863
  <ReqoreButton
753
864
  className='options-readfirst-done'
@@ -902,6 +1013,9 @@ export const CompactRow = memo(
902
1013
  </ReqoreButton>
903
1014
  );
904
1015
  })}
1016
+ {inlineOptionActions.map((action, index) =>
1017
+ renderInjectedOptionAction(action, index)
1018
+ )}
905
1019
  {/* Clear-value sits before the More menu — the card analog of the
906
1020
  inline row's Clear. Empties the value (keeps the field). */}
907
1021
  {hasValue && !readOnly ?
@@ -1304,6 +1418,31 @@ export const CompactRow = memo(
1304
1418
  {infoToggle}
1305
1419
  </StyledActionSlot>
1306
1420
  : null}
1421
+ {inlineOptionActions.map((action, index) =>
1422
+ renderInjectedOptionAction(action, index, 'tiny')
1423
+ )}
1424
+ {injectedOptionActionMenuItems.length ?
1425
+ <StyledActionSlot
1426
+ className='options-readfirst-actions-slot'
1427
+ $width={26}
1428
+ // The read row opens the editor when clicked. Without this the tap
1429
+ // that opens this menu also expands the row, which unmounts the
1430
+ // menu — on touch that made the collapsed actions unreachable,
1431
+ // the exact problem the collapse exists to solve.
1432
+ onClick={(event: React.MouseEvent<HTMLElement>) => event.stopPropagation()}
1433
+ >
1434
+ <ReqoreDropdown
1435
+ className='options-injected-actions-menu'
1436
+ icon='MoreFill'
1437
+ flat
1438
+ minimal
1439
+ fixed
1440
+ size='tiny'
1441
+ tooltip='Field actions'
1442
+ items={injectedOptionActionMenuItems}
1443
+ />
1444
+ </StyledActionSlot>
1445
+ : null}
1307
1446
  {/* The revert affordance lives in the status-dot column (a changed field
1308
1447
  swaps its dot for the revert icon) so it sits at the same fixed x as
1309
1448
  the dot — out of the value's way instead of floating over it. */}