@qoretechnologies/reqraft 0.10.51 → 0.10.52

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 (50) hide show
  1. package/dist/components/form/expressions/ExpressionField.d.ts +4 -2
  2. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  3. package/dist/components/form/expressions/ExpressionField.js +2 -2
  4. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  5. package/dist/components/form/expressions/builder/argumentWrapper.d.ts +13 -2
  6. package/dist/components/form/expressions/builder/argumentWrapper.d.ts.map +1 -1
  7. package/dist/components/form/expressions/builder/argumentWrapper.js +61 -3
  8. package/dist/components/form/expressions/builder/argumentWrapper.js.map +1 -1
  9. package/dist/components/form/expressions/builder/index.d.ts +4 -2
  10. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  11. package/dist/components/form/expressions/builder/index.js +96 -15
  12. package/dist/components/form/expressions/builder/index.js.map +1 -1
  13. package/dist/components/form/expressions/builder/moveToPositionStrip.d.ts +19 -0
  14. package/dist/components/form/expressions/builder/moveToPositionStrip.d.ts.map +1 -0
  15. package/dist/components/form/expressions/builder/moveToPositionStrip.js +62 -0
  16. package/dist/components/form/expressions/builder/moveToPositionStrip.js.map +1 -0
  17. package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
  18. package/dist/components/form/expressions/mockExpressions.js +18 -0
  19. package/dist/components/form/expressions/mockExpressions.js.map +1 -1
  20. package/dist/components/form/expressions/types.d.ts +8 -0
  21. package/dist/components/form/expressions/types.d.ts.map +1 -1
  22. package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js +3 -2
  23. package/dist/components/form/fields/schema-definition/CatalogNodeEditor.js.map +1 -1
  24. package/dist/components/form/fields/schema-definition/MigrationsTab.js +3 -3
  25. package/dist/components/form/fields/schema-definition/MigrationsTab.js.map +1 -1
  26. package/dist/components/form/fields/schema-definition/helpers.d.ts +0 -2
  27. package/dist/components/form/fields/schema-definition/helpers.d.ts.map +1 -1
  28. package/dist/components/form/fields/schema-definition/helpers.js +1 -20
  29. package/dist/components/form/fields/schema-definition/helpers.js.map +1 -1
  30. package/dist/components/form/fields/template/TemplateField.d.ts +33 -4
  31. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  32. package/dist/components/form/fields/template/TemplateField.js +57 -7
  33. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  34. package/dist/helpers/common.d.ts +4 -0
  35. package/dist/helpers/common.d.ts.map +1 -1
  36. package/dist/helpers/common.js +30 -1
  37. package/dist/helpers/common.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/components/form/expressions/ExpressionField.tsx +5 -1
  40. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +386 -0
  41. package/src/components/form/expressions/builder/argumentWrapper.tsx +137 -11
  42. package/src/components/form/expressions/builder/index.tsx +153 -5
  43. package/src/components/form/expressions/builder/moveToPositionStrip.tsx +156 -0
  44. package/src/components/form/expressions/mockExpressions.ts +18 -0
  45. package/src/components/form/expressions/types.ts +10 -0
  46. package/src/components/form/fields/schema-definition/CatalogNodeEditor.tsx +1 -1
  47. package/src/components/form/fields/schema-definition/MigrationsTab.tsx +1 -1
  48. package/src/components/form/fields/schema-definition/helpers.ts +0 -9
  49. package/src/components/form/fields/template/TemplateField.tsx +112 -4
  50. package/src/helpers/common.ts +31 -0
@@ -9,6 +9,7 @@ import {
9
9
  ReqoreDropdown,
10
10
  ReqoreErrorBoundary,
11
11
  ReqoreMenu,
12
+ ReqoreMenuItem,
12
13
  ReqoreMenuSection,
13
14
  ReqoreMessage,
14
15
  ReqorePopover,
@@ -16,6 +17,8 @@ import {
16
17
  } from '@qoretechnologies/reqore';
17
18
  import { IReqoreButtonProps } from '@qoretechnologies/reqore/dist/components/Button';
18
19
  import { IReqoreDropdownProps } from '@qoretechnologies/reqore/dist/components/Dropdown';
20
+ import { IReqoreMenuItemProps } from '@qoretechnologies/reqore/dist/components/Menu/item';
21
+ import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
19
22
  import ReqoreMenuDivider, {
20
23
  IReqoreMenuDividerProps,
21
24
  } from '@qoretechnologies/reqore/dist/components/Menu/divider';
@@ -26,7 +29,7 @@ import {
26
29
  } from '@qoretechnologies/reqore/dist/components/Textarea';
27
30
  import { IQorusFormFieldSchemaBase, TQorusType } from '@qoretechnologies/ts-toolkit';
28
31
  import { size } from 'lodash';
29
- import { memo, useCallback, useEffect, useMemo, useState } from 'react';
32
+ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
30
33
  import { useUpdateEffect } from 'react-use';
31
34
  import {
32
35
  filterTemplatesByType as templatesFilterFunc,
@@ -44,7 +47,7 @@ import { ExpressionBuilder, IExpressionBuilderProps } from '../../expressions/bu
44
47
  // Direct import — the cycle (TemplateField → ExpressionField → builder →
45
48
  // TemplateField) is render-time only, safe like the other Field cycles.
46
49
  import { ExpressionField } from '../../expressions/ExpressionField';
47
- import { IExpression } from '../../expressions/types';
50
+ import { IExpression, TExpressionReorder } from '../../expressions/types';
48
51
  import { useExpressions } from '../../expressions/useExpressions';
49
52
  import { AutoFormField as Auto, IQorusType as IQorusFormType } from '../auto/AutoFormField';
50
53
  import BooleanFormField from '../boolean/Boolean';
@@ -134,6 +137,14 @@ export interface ITemplateFieldProps extends Partial<
134
137
  isDefaultFunction?: boolean;
135
138
  isDefaultTemplate?: boolean;
136
139
  menuItems?: TCustomTemplateItems;
140
+ /**
141
+ * SEAM (reqraft, additive): a collapsed section in the field's `⋮` menu,
142
+ * rendered before "Set Custom Value"; a row click closes the menu. The
143
+ * expression builder puts its operand reorder actions here.
144
+ */
145
+ menuActions?: ITemplateMenuActions;
146
+ /** SEAM (reqraft, additive): forwarded to a nested expression builder. */
147
+ reorder?: TExpressionReorder;
137
148
  /**
138
149
  * SEAM (reqraft, additive): render expression mode through the
139
150
  * `ExpressionField` shell — Visual (the ported builder) + Text (the
@@ -184,6 +195,92 @@ export type TCustomTemplateItems = (
184
195
  | (IReqoreMenuDividerProps & { isDivider?: true })
185
196
  )[];
186
197
 
198
+ /**
199
+ * The `menuActions` seam: a collapsed section of menu items, dividers, nested
200
+ * sections, or custom content — rendered with the popover's close callback so
201
+ * an inline control can shut the menu after acting.
202
+ */
203
+ export interface ITemplateMenuActions {
204
+ label: string;
205
+ icon?: IReqoreIconName;
206
+ className?: string;
207
+ items: (
208
+ | IReqoreMenuItemProps
209
+ | (IReqoreMenuDividerProps & { isDivider: true })
210
+ | (ITemplateMenuActions & { isSection: true })
211
+ | { isCustom: true; content: (closePopover?: () => void) => React.ReactNode }
212
+ )[];
213
+ }
214
+
215
+ const renderMenuActionRows = (
216
+ actions: ITemplateMenuActions,
217
+ closePopover: (() => void) | undefined,
218
+ size: IReqoreButtonProps['size']
219
+ ): React.ReactNode =>
220
+ actions.items.map((item, index) => {
221
+ if ('isDivider' in item) {
222
+ return <ReqoreMenuDivider key={index} {...item} />;
223
+ }
224
+
225
+ if ('isCustom' in item) {
226
+ return <React.Fragment key={index}>{item.content(closePopover)}</React.Fragment>;
227
+ }
228
+
229
+ if ('isSection' in item) {
230
+ return (
231
+ <ReqoreMenuSection
232
+ key={index}
233
+ label={item.label}
234
+ icon={item.icon}
235
+ className={item.className}
236
+ isCollapsed
237
+ transparent
238
+ size={size}
239
+ >
240
+ {renderMenuActionRows(item, closePopover, size)}
241
+ </ReqoreMenuSection>
242
+ );
243
+ }
244
+
245
+ return (
246
+ <ReqoreMenuItem
247
+ {...item}
248
+ key={index}
249
+ onClick={(event, itemId) => {
250
+ item.onClick?.(event, itemId, closePopover);
251
+ closePopover?.();
252
+ }}
253
+ />
254
+ );
255
+ });
256
+
257
+ // A direct child of `ReqoreMenu`, like `CustomMenuItems`, so the menu hands it
258
+ // the popover's `closePopover` — a section does not pass it on to its rows.
259
+ const MenuActionsSection = memo(
260
+ ({
261
+ actions,
262
+ closePopover,
263
+ size,
264
+ ...rest
265
+ }: {
266
+ actions: ITemplateMenuActions;
267
+ closePopover?: () => void;
268
+ size?: IReqoreButtonProps['size'];
269
+ }) => (
270
+ <ReqoreMenuSection
271
+ label={actions.label}
272
+ icon={actions.icon}
273
+ isCollapsed
274
+ transparent
275
+ className='template-menu-actions'
276
+ size={size}
277
+ {...rest}
278
+ >
279
+ {renderMenuActionRows(actions, closePopover, size)}
280
+ </ReqoreMenuSection>
281
+ )
282
+ );
283
+
187
284
  export const CustomMenuItems = memo(
188
285
  ({
189
286
  items,
@@ -317,6 +414,8 @@ export const TemplateField = memo(
317
414
  level,
318
415
  className,
319
416
  menuItems,
417
+ menuActions,
418
+ reorder,
320
419
  label,
321
420
  ...rest
322
421
  }: ITemplateFieldProps) => {
@@ -579,6 +678,8 @@ export const TemplateField = memo(
579
678
  const showFunctionsDropdown =
580
679
  allowFunctions && !hasOnlyAllowedValues && !rest.readonly && !internalIsFunction;
581
680
  const showTemplatesButton = showTemplateToggle && !isTemplate;
681
+ // The "Set value" label promises a way to set one — reorder rows alone don't.
682
+ const hasValueRows = showFunctionsDropdown || showTemplatesButton || size(menuItems) > 0;
582
683
 
583
684
  if (hasOnlyExpressions) {
584
685
  return showFunctionsDropdown ? (
@@ -609,7 +710,7 @@ export const TemplateField = memo(
609
710
  ) : null;
610
711
  }
611
712
 
612
- if (showFunctionsDropdown || showTemplatesButton || size(menuItems) > 0) {
713
+ if (hasValueRows || size(menuActions?.items) > 0) {
613
714
  return (
614
715
  <ReqorePopover
615
716
  component={ReqoreButton}
@@ -631,7 +732,7 @@ export const TemplateField = memo(
631
732
  // sibling action buttons (reqore alignSelf; replaces a reqraft
632
733
  // `align-self !important` override of this button).
633
734
  alignSelf: 'center',
634
- label: hasInputAffordance ? undefined : 'Set value',
735
+ label: hasInputAffordance || !hasValueRows ? undefined : 'Set value',
635
736
  style:
636
737
  hasInputAffordance ?
637
738
  {
@@ -687,6 +788,10 @@ export const TemplateField = memo(
687
788
  </ReqoreButton>
688
789
  ) : null}
689
790
 
791
+ {size(menuActions?.items) > 0 ? (
792
+ <MenuActionsSection actions={menuActions} size={rest.size} />
793
+ ) : null}
794
+
690
795
  {size(menuItems) > 0 ? (
691
796
  <CustomMenuItems
692
797
  items={menuItems}
@@ -715,6 +820,7 @@ export const TemplateField = memo(
715
820
  type,
716
821
  value,
717
822
  menuItems,
823
+ menuActions,
718
824
  internalIsFunction,
719
825
  hasInputAffordance,
720
826
  ]);
@@ -758,6 +864,7 @@ export const TemplateField = memo(
758
864
  serverHandled={rest.server_expression_handling}
759
865
  extraActions={extraActions}
760
866
  size={rest.size}
867
+ reorder={reorder}
761
868
  />
762
869
  </ReqoreErrorBoundary>
763
870
  {renderControls()}
@@ -783,6 +890,7 @@ export const TemplateField = memo(
783
890
  expressionsUrl={rest.expressions_url}
784
891
  serverHandled={rest.server_expression_handling}
785
892
  extraActions={extraActions}
893
+ reorder={reorder}
786
894
  />
787
895
  </ReqoreErrorBoundary>
788
896
  {renderControls()}
@@ -275,3 +275,34 @@ export const getAddress = (v?: string): string => {
275
275
  // We need to join back the rest of the address in case there were :// in it
276
276
  return valueList.slice(1).join('://');
277
277
  };
278
+
279
+ // ─── List helpers ─────────────────────────────────────────────────────────────
280
+
281
+ /** Immutably moves a list element from `from` to `to`. */
282
+ export const moveItem = <T>(list: T[], from: number, to: number): T[] => {
283
+ if (from === to || from < 0 || to < 0 || from >= list.length) return list;
284
+ const next = [...list];
285
+ const [item] = next.splice(from, 1);
286
+ next.splice(Math.min(to, next.length), 0, item);
287
+ return next;
288
+ };
289
+
290
+ /** `1` → `1st`, `2` → `2nd`, `11` → `11th`. */
291
+ export const ordinal = (n: number): string => {
292
+ const mod100 = n % 100;
293
+
294
+ if (mod100 >= 11 && mod100 <= 13) {
295
+ return `${n}th`;
296
+ }
297
+
298
+ switch (n % 10) {
299
+ case 1:
300
+ return `${n}st`;
301
+ case 2:
302
+ return `${n}nd`;
303
+ case 3:
304
+ return `${n}rd`;
305
+ default:
306
+ return `${n}th`;
307
+ }
308
+ };