@qoretechnologies/reqraft 0.10.53 → 0.10.54-pr.115.ge2d364c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +25 -0
- package/.tasks/INDEX.md +1 -0
- package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
- package/README.md +1 -1
- package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
- package/design/RENDER_EXPRESSION_TRANSPORT.md +18 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
- package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/index.d.ts +4 -0
- package/dist/components/dpqlEditor/index.d.ts.map +1 -1
- package/dist/components/dpqlEditor/index.js +5 -1
- package/dist/components/dpqlEditor/index.js.map +1 -1
- package/dist/components/dpqlEditor/types.d.ts +29 -2
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
- package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
- package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +186 -21
- 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 +5 -2
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +159 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +376 -75
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js +38 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +17 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +61 -11
- 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 +7 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.d.ts +42 -1
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +74 -7
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +13 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
- package/dist/components/form/engine/rendererTypes.js.map +1 -1
- package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
- package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
- package/dist/components/form/engine/rowMenuContext.js +103 -0
- package/dist/components/form/engine/rowMenuContext.js.map +1 -0
- package/dist/components/form/engine/typeChoices.d.ts +37 -0
- package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
- package/dist/components/form/engine/typeChoices.js +25 -0
- package/dist/components/form/engine/typeChoices.js.map +1 -0
- package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
- package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
- package/dist/components/form/expressions/ExpressionField.js +181 -33
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +97 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
- package/dist/components/form/expressions/argumentPresence.js +203 -0
- package/dist/components/form/expressions/argumentPresence.js.map +1 -0
- package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
- package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts +15 -2
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +19 -57
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +17 -0
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +1 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +75 -9
- package/dist/components/form/expressions/dpqlMockLsp.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +94 -19
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +3 -1
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +14 -2
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +13 -2
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +11 -0
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +84 -5
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts +31 -1
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +507 -40
- package/dist/components/form/fields/template/TemplateField.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/smartEditor/__fixtures__/mockLspServer.d.ts +1 -0
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +1 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +6 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +1 -1
- package/dist/helpers/dpqlDetection.d.ts +58 -0
- package/dist/helpers/dpqlDetection.d.ts.map +1 -0
- package/dist/helpers/dpqlDetection.js +164 -0
- package/dist/helpers/dpqlDetection.js.map +1 -0
- package/dist/helpers/optionUiTypes.d.ts +1 -1
- package/dist/helpers/options.d.ts +37 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +78 -1
- package/dist/helpers/options.js.map +1 -1
- package/dist/helpers/templateItems.d.ts +47 -0
- package/dist/helpers/templateItems.d.ts.map +1 -0
- package/dist/helpers/templateItems.js +88 -0
- package/dist/helpers/templateItems.js.map +1 -0
- package/dist/helpers/templates.d.ts +21 -0
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +49 -3
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +56 -9
- package/dist/helpers/validations.js.map +1 -1
- package/dist/hooks/useArgSchema.d.ts +2 -1
- package/dist/hooks/useArgSchema.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.d.ts +3 -1
- package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.js +47 -9
- package/dist/hooks/useFetch/useFetch.js.map +1 -1
- package/dist/hooks/useTemplates.d.ts +20 -0
- package/dist/hooks/useTemplates.d.ts.map +1 -1
- package/dist/hooks/useTemplates.js +34 -2
- package/dist/hooks/useTemplates.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/stories/storyNetwork.d.ts +36 -0
- package/dist/stories/storyNetwork.d.ts.map +1 -0
- package/dist/stories/storyNetwork.js +82 -0
- package/dist/stories/storyNetwork.js.map +1 -0
- package/dist/utils/fetch.d.ts +151 -1
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +256 -44
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/lspClient.d.ts +6 -0
- package/dist/utils/lspClient.d.ts.map +1 -1
- package/dist/utils/lspClient.js +62 -11
- package/dist/utils/lspClient.js.map +1 -1
- package/dist/utils/websocket.d.ts +6 -0
- package/dist/utils/websocket.d.ts.map +1 -1
- package/dist/utils/websocket.js +15 -7
- package/dist/utils/websocket.js.map +1 -1
- package/package.json +2 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +0 -2
- package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
- package/src/components/dpqlEditor/index.ts +4 -0
- package/src/components/dpqlEditor/types.ts +26 -2
- package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
- package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
- package/src/components/form/engine/CompactRow.tsx +231 -11
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +331 -30
- package/src/components/form/engine/FormEngine.tsx +504 -87
- package/src/components/form/engine/FormFieldsSkeleton.tsx +76 -0
- package/src/components/form/engine/OptionFieldMessages.tsx +89 -11
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +11 -0
- package/src/components/form/engine/compactToolbarContext.ts +1 -1
- package/src/components/form/engine/readFirst.ts +86 -7
- package/src/components/form/engine/rendererTypes.ts +12 -0
- package/src/components/form/engine/rowMenuContext.ts +159 -0
- package/src/components/form/engine/typeChoices.ts +55 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +828 -31
- package/src/components/form/expressions/ExpressionField.tsx +234 -29
- package/src/components/form/expressions/argumentPresence.ts +200 -0
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +31 -75
- package/src/components/form/expressions/dpqlMockLsp.ts +81 -1
- package/src/components/form/expressions/renderExpressionToText.ts +106 -17
- package/src/components/form/fields/Field.tsx +3 -2
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +12 -2
- package/src/components/form/fields/rich-text/RichText.tsx +112 -19
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.tsx +667 -95
- package/src/components/form/index.tsx +1 -0
- package/src/components/log/Log.stories.tsx +2 -1
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +6 -1
- package/src/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/options.ts +70 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templates.ts +51 -2
- package/src/helpers/validations.ts +59 -9
- package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
- package/src/hooks/useFetch/useFetch.tsx +53 -7
- package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
- package/src/hooks/useTemplates.ts +41 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
- package/src/index.tsx +14 -1
- package/src/stores/currentUser/currentUser.stories.tsx +2 -1
- package/src/stories/storyNetwork.ts +92 -0
- package/src/utils/fetch.ts +311 -21
- package/src/utils/lspClient.ts +68 -11
- package/src/utils/websocket.ts +20 -9
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { IReqoreDropdownItem } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
3
|
+
/**
|
|
4
|
+
* SEAM (reqraft): an editor publishes its own actions into the row's ⋮ menu.
|
|
5
|
+
*
|
|
6
|
+
* A row already renders one overflow menu — Edit fullscreen, Remove field, and
|
|
7
|
+
* any `optionActions` the host injected — and the comment on it has always said
|
|
8
|
+
* why: *"this row already has a menu, so they reuse it rather than adding a
|
|
9
|
+
* second one beside it."* An EDITOR could not reach that menu, so an editor
|
|
10
|
+
* with affordances of its own had no choice but to draw a second ⋮ inside the
|
|
11
|
+
* value cell. The IDE's template field does exactly that, and the two menus
|
|
12
|
+
* ended up side by side on one control, at slightly different widths.
|
|
13
|
+
*
|
|
14
|
+
* `optionActions` does not solve it: that seam is host configuration, resolved
|
|
15
|
+
* from the option's name and schema before the editor exists, so it cannot know
|
|
16
|
+
* what the editor can offer *now* — whether its templates have loaded, whether
|
|
17
|
+
* the value is already an expression, which custom items the field was given.
|
|
18
|
+
*
|
|
19
|
+
* ## Why registration is keyed, and by whom
|
|
20
|
+
*
|
|
21
|
+
* Menu items carry `onClick` handlers, so they are a new array on every render
|
|
22
|
+
* and cannot be compared by value. An editor therefore publishes a `key`
|
|
23
|
+
* alongside them: a primitive describing WHICH items it is currently offering
|
|
24
|
+
* (`'expression,template'`). The row re-renders only when a publisher's key
|
|
25
|
+
* changes, so a re-rendering editor cannot drive the row into a state loop.
|
|
26
|
+
*
|
|
27
|
+
* Each registration also belongs to ONE publisher. A single shared slot let
|
|
28
|
+
* two publishers in the same row — an expression builder's operands — overwrite
|
|
29
|
+
* each other's key on every render, which re-rendered the row and both of them,
|
|
30
|
+
* which published again: a synchronous render loop that froze the page. And a
|
|
31
|
+
* slot that outlived its editor kept that editor's handlers after it unmounted,
|
|
32
|
+
* so a row closed and reopened offered items that did nothing. Registrations
|
|
33
|
+
* are therefore per publisher, withdrawn on unmount, and a click always reaches
|
|
34
|
+
* the handler from the publisher's latest render.
|
|
35
|
+
*
|
|
36
|
+
* Publish with `useRowMenuPublisher`; provide with `useRowMenuRegistry`.
|
|
37
|
+
*/
|
|
38
|
+
export interface IRowMenuRegistration {
|
|
39
|
+
/**
|
|
40
|
+
* Publish a publisher's menu items into the containing row's ⋮.
|
|
41
|
+
*
|
|
42
|
+
* @param publisherId identifies the publishing editor (one per mounted editor)
|
|
43
|
+
* @param key a primitive describing which items are being offered; the row
|
|
44
|
+
* re-renders only when it changes
|
|
45
|
+
* @param items the items to merge, or an empty array to contribute none
|
|
46
|
+
*/
|
|
47
|
+
registerRowMenuItems: (publisherId: string, key: string, items: IReqoreDropdownItem[]) => void;
|
|
48
|
+
/** Withdraw a publisher's items — when it unmounts or leaves the row. */
|
|
49
|
+
unregisterRowMenuItems: (publisherId: string) => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Absent outside a compact row — the classic (non-compact) render path has no
|
|
53
|
+
* row menu to merge into, and an editor there keeps drawing its own control.
|
|
54
|
+
*/
|
|
55
|
+
export declare const RowMenuContext: import("react").Context<IRowMenuRegistration>;
|
|
56
|
+
/**
|
|
57
|
+
* The row-menu channel for the row this editor is inside, or `undefined` when
|
|
58
|
+
* there is no row menu to publish into.
|
|
59
|
+
*
|
|
60
|
+
* An editor that finds it should render no menu of its own; one that does not
|
|
61
|
+
* must keep rendering its own, or its actions become unreachable.
|
|
62
|
+
*/
|
|
63
|
+
export declare const useRowMenu: () => IRowMenuRegistration | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Publish `items` into `rowMenu` for as long as the calling editor is mounted.
|
|
66
|
+
* `rowMenu` is passed rather than read here so an editor can claim the channel
|
|
67
|
+
* and hide it from what it renders (see `TemplateField`).
|
|
68
|
+
*/
|
|
69
|
+
export declare const useRowMenuPublisher: (rowMenu: IRowMenuRegistration | undefined, key: string, items: IReqoreDropdownItem[]) => void;
|
|
70
|
+
/**
|
|
71
|
+
* The row side of the channel: the registration to provide, and the items the
|
|
72
|
+
* row's ⋮ should list.
|
|
73
|
+
*/
|
|
74
|
+
export declare const useRowMenuRegistry: () => {
|
|
75
|
+
rowMenu: IRowMenuRegistration;
|
|
76
|
+
items: IReqoreDropdownItem[];
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=rowMenuContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rowMenuContext.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/rowMenuContext.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wDAAwD,CAAC;AAY7F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,oBAAoB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,IAAI,CAAC;IAC/F,yEAAyE;IACzE,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,+CAA6D,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAAO,oBAAoB,GAAG,SAAuC,CAAC;AAE7F;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,YACrB,oBAAoB,GAAG,SAAS,OACpC,MAAM,SACJ,mBAAmB,EAAE,KAC3B,IAQF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO;IACpC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAwD9B,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
14
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
15
|
+
if (ar || !(i in from)) {
|
|
16
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
17
|
+
ar[i] = from[i];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.useRowMenuRegistry = exports.useRowMenuPublisher = exports.useRowMenu = exports.RowMenuContext = void 0;
|
|
24
|
+
var react_1 = require("react");
|
|
25
|
+
/**
|
|
26
|
+
* Absent outside a compact row — the classic (non-compact) render path has no
|
|
27
|
+
* row menu to merge into, and an editor there keeps drawing its own control.
|
|
28
|
+
*/
|
|
29
|
+
exports.RowMenuContext = (0, react_1.createContext)(undefined);
|
|
30
|
+
/**
|
|
31
|
+
* The row-menu channel for the row this editor is inside, or `undefined` when
|
|
32
|
+
* there is no row menu to publish into.
|
|
33
|
+
*
|
|
34
|
+
* An editor that finds it should render no menu of its own; one that does not
|
|
35
|
+
* must keep rendering its own, or its actions become unreachable.
|
|
36
|
+
*/
|
|
37
|
+
var useRowMenu = function () { return (0, react_1.useContext)(exports.RowMenuContext); };
|
|
38
|
+
exports.useRowMenu = useRowMenu;
|
|
39
|
+
/**
|
|
40
|
+
* Publish `items` into `rowMenu` for as long as the calling editor is mounted.
|
|
41
|
+
* `rowMenu` is passed rather than read here so an editor can claim the channel
|
|
42
|
+
* and hide it from what it renders (see `TemplateField`).
|
|
43
|
+
*/
|
|
44
|
+
var useRowMenuPublisher = function (rowMenu, key, items) {
|
|
45
|
+
var publisherId = (0, react_1.useId)();
|
|
46
|
+
(0, react_1.useEffect)(function () {
|
|
47
|
+
rowMenu === null || rowMenu === void 0 ? void 0 : rowMenu.registerRowMenuItems(publisherId, key, items);
|
|
48
|
+
}, [rowMenu, publisherId, key, items]);
|
|
49
|
+
(0, react_1.useEffect)(function () { return function () { return rowMenu === null || rowMenu === void 0 ? void 0 : rowMenu.unregisterRowMenuItems(publisherId); }; }, [rowMenu, publisherId]);
|
|
50
|
+
};
|
|
51
|
+
exports.useRowMenuPublisher = useRowMenuPublisher;
|
|
52
|
+
/**
|
|
53
|
+
* The row side of the channel: the registration to provide, and the items the
|
|
54
|
+
* row's ⋮ should list.
|
|
55
|
+
*/
|
|
56
|
+
var useRowMenuRegistry = function () {
|
|
57
|
+
// Which publishers are offering which item SET — the only state, so only a
|
|
58
|
+
// change of set re-renders the row.
|
|
59
|
+
var _a = (0, react_1.useState)({}), keys = _a[0], setKeys = _a[1];
|
|
60
|
+
// Each publisher's items from its latest render, read at click time.
|
|
61
|
+
var latest = (0, react_1.useRef)({});
|
|
62
|
+
var registerRowMenuItems = (0, react_1.useCallback)(function (publisherId, key, items) {
|
|
63
|
+
latest.current[publisherId] = items;
|
|
64
|
+
setKeys(function (previous) {
|
|
65
|
+
var _a;
|
|
66
|
+
return previous[publisherId] === key ? previous : __assign(__assign({}, previous), (_a = {}, _a[publisherId] = key, _a));
|
|
67
|
+
});
|
|
68
|
+
}, []);
|
|
69
|
+
var unregisterRowMenuItems = (0, react_1.useCallback)(function (publisherId) {
|
|
70
|
+
delete latest.current[publisherId];
|
|
71
|
+
setKeys(function (previous) {
|
|
72
|
+
if (!(publisherId in previous)) {
|
|
73
|
+
return previous;
|
|
74
|
+
}
|
|
75
|
+
var next = __assign({}, previous);
|
|
76
|
+
delete next[publisherId];
|
|
77
|
+
return next;
|
|
78
|
+
});
|
|
79
|
+
}, []);
|
|
80
|
+
var rowMenu = (0, react_1.useMemo)(function () { return ({ registerRowMenuItems: registerRowMenuItems, unregisterRowMenuItems: unregisterRowMenuItems }); }, [registerRowMenuItems, unregisterRowMenuItems]);
|
|
81
|
+
var items = (0, react_1.useMemo)(function () {
|
|
82
|
+
return Object.keys(keys).flatMap(function (publisherId) {
|
|
83
|
+
var _a;
|
|
84
|
+
return ((_a = latest.current[publisherId]) !== null && _a !== void 0 ? _a : []).map(function (item, index) {
|
|
85
|
+
return item.onClick
|
|
86
|
+
? __assign(__assign({}, item), {
|
|
87
|
+
// Same key, same item set, so the index still names this item;
|
|
88
|
+
// the handler is whatever the publisher rendered last.
|
|
89
|
+
onClick: (function () {
|
|
90
|
+
var _a, _b, _c;
|
|
91
|
+
var args = [];
|
|
92
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
93
|
+
args[_i] = arguments[_i];
|
|
94
|
+
}
|
|
95
|
+
return (_c = (_b = (_a = latest.current[publisherId]) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.onClick) === null || _c === void 0 ? void 0 : _c.call.apply(_c, __spreadArray([_b], args, false));
|
|
96
|
+
}) }) : item;
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}, [keys]);
|
|
100
|
+
return { rowMenu: rowMenu, items: items };
|
|
101
|
+
};
|
|
102
|
+
exports.useRowMenuRegistry = useRowMenuRegistry;
|
|
103
|
+
//# sourceMappingURL=rowMenuContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rowMenuContext.js","sourceRoot":"","sources":["../../../../src/components/form/engine/rowMenuContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which options offer the per-type choices ("Integer", "Text", "Hash", …) in
|
|
3
|
+
* the row's menu.
|
|
4
|
+
*
|
|
5
|
+
* A field that pins no concrete type has to give the author some way to say
|
|
6
|
+
* what kind of value they mean, and the row menu is where that lives — the
|
|
7
|
+
* alternative, a standalone *"Please select data type"* control, replaces a
|
|
8
|
+
* typable field with a pick-only one.
|
|
9
|
+
*
|
|
10
|
+
* The rule was `ui_type === 'any'`, which is one of the three ways a schema
|
|
11
|
+
* says "untyped", so the choices went missing on the other two:
|
|
12
|
+
*
|
|
13
|
+
* - **`type: 'auto'` with no `ui_type` at all.** This is how the server
|
|
14
|
+
* declares an assertion's Expected Value, so that field never offered them.
|
|
15
|
+
* They appeared only inside an expression, where the OPERAND's schema does
|
|
16
|
+
* say `ui_type: 'any'` — so the same option offered them in the Visual view
|
|
17
|
+
* and not in the Text view, which reads as the menu losing items.
|
|
18
|
+
* - **A host's own untyped editor.** The IDE renders Expected Value through
|
|
19
|
+
* `test-reference`, a `ui_type` reqraft knows nothing about; the field is no
|
|
20
|
+
* less untyped for having a custom editor.
|
|
21
|
+
*
|
|
22
|
+
* `ui_type` is asked FIRST and on its own: a schema that names a concrete
|
|
23
|
+
* editor (`ui_type: 'string'`) has pinned the field even if its storage `type`
|
|
24
|
+
* is still `auto`.
|
|
25
|
+
*/
|
|
26
|
+
import { IQorusFormField } from '@qoretechnologies/ts-toolkit';
|
|
27
|
+
/** The spellings that mean "no concrete type" wherever they appear. */
|
|
28
|
+
export declare const BuiltInAnyLikeTypes: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Does this option leave its type up to the author?
|
|
31
|
+
*
|
|
32
|
+
* @param option the option's schema entry
|
|
33
|
+
* @param extraUiTypes host `ui_type` names that are untyped editors of their
|
|
34
|
+
* own — see `FormEngine`'s `anyLikeUiTypes`
|
|
35
|
+
*/
|
|
36
|
+
export declare const offersTypeChoices: (option: Partial<IQorusFormField> | undefined, extraUiTypes?: string[]) => boolean;
|
|
37
|
+
//# sourceMappingURL=typeChoices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeChoices.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/typeChoices.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,UAAkB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WACpB,QAAQ,eAAe,CAAC,GAAG,SAAS,iBAC7B,MAAM,EAAE,KACtB,OAaF,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.offersTypeChoices = exports.BuiltInAnyLikeTypes = void 0;
|
|
4
|
+
/** The spellings that mean "no concrete type" wherever they appear. */
|
|
5
|
+
exports.BuiltInAnyLikeTypes = ['any', 'auto'];
|
|
6
|
+
/**
|
|
7
|
+
* Does this option leave its type up to the author?
|
|
8
|
+
*
|
|
9
|
+
* @param option the option's schema entry
|
|
10
|
+
* @param extraUiTypes host `ui_type` names that are untyped editors of their
|
|
11
|
+
* own — see `FormEngine`'s `anyLikeUiTypes`
|
|
12
|
+
*/
|
|
13
|
+
var offersTypeChoices = function (option, extraUiTypes) {
|
|
14
|
+
if (!option) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
var uiType = option.ui_type;
|
|
18
|
+
if (uiType) {
|
|
19
|
+
return exports.BuiltInAnyLikeTypes.includes(uiType) || !!(extraUiTypes === null || extraUiTypes === void 0 ? void 0 : extraUiTypes.includes(uiType));
|
|
20
|
+
}
|
|
21
|
+
// No `ui_type`: the storage type is the only thing that can pin the field.
|
|
22
|
+
return exports.BuiltInAnyLikeTypes.includes(option.type);
|
|
23
|
+
};
|
|
24
|
+
exports.offersTypeChoices = offersTypeChoices;
|
|
25
|
+
//# sourceMappingURL=typeChoices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeChoices.js","sourceRoot":"","sources":["../../../../src/components/form/engine/typeChoices.ts"],"names":[],"mappings":";;;AA4BA,uEAAuE;AAC1D,QAAA,mBAAmB,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAEnD;;;;;;GAMG;AACI,IAAM,iBAAiB,GAAG,UAC/B,MAA4C,EAC5C,YAAuB;IAEvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAM,MAAM,GAAI,MAA+B,CAAC,OAAO,CAAC;IAExD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,2BAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,QAAQ,CAAC,MAAM,CAAC,CAAA,CAAC;IAClF,CAAC;IAED,2EAA2E;IAC3E,OAAO,2BAAmB,CAAC,QAAQ,CAAE,MAA4B,CAAC,IAAc,CAAC,CAAC;AACpF,CAAC,CAAC;AAhBW,QAAA,iBAAiB,qBAgB5B"}
|
|
@@ -38,10 +38,16 @@ export interface IExpressionFieldProps {
|
|
|
38
38
|
recordType?: string;
|
|
39
39
|
/** Initial editor mode (default `visual`). */
|
|
40
40
|
defaultMode?: TExpressionMode;
|
|
41
|
+
/**
|
|
42
|
+
* SEAM (reqraft): the host's per-`ui_type` editors, forwarded to the
|
|
43
|
+
* builder's operand fields. Without them an operand typed with one of the
|
|
44
|
+
* CONSUMER's ui_types renders "Unknown type!".
|
|
45
|
+
*/
|
|
46
|
+
componentOverrides?: Record<string, React.FC<any>>;
|
|
41
47
|
size?: string;
|
|
42
48
|
/** Forwarded to the builder — operand reordering for varargs expressions. */
|
|
43
49
|
reorder?: TExpressionReorder;
|
|
44
50
|
}
|
|
45
|
-
export declare const ExpressionField: import("react").MemoExoticComponent<({ value, onChange, type, returnType, readOnly, expressions: expressionsOverride, expressionsUrl, localTemplates, serverHandled, extraActions, provider, recordType, defaultMode, size, reorder, }: IExpressionFieldProps) => import("react/jsx-runtime").JSX.Element>;
|
|
51
|
+
export declare const ExpressionField: import("react").MemoExoticComponent<({ value, onChange, type, returnType, readOnly, expressions: expressionsOverride, expressionsUrl, localTemplates, serverHandled, extraActions, provider, recordType, defaultMode, size, componentOverrides, reorder, }: IExpressionFieldProps) => import("react/jsx-runtime").JSX.Element>;
|
|
46
52
|
export default ExpressionField;
|
|
47
53
|
//# sourceMappingURL=ExpressionField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpressionField.d.ts","sourceRoot":"","sources":["../../../../src/components/form/expressions/ExpressionField.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"ExpressionField.d.ts","sourceRoot":"","sources":["../../../../src/components/form/expressions/ExpressionField.tsx"],"names":[],"mappings":";AAaA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAC;AAGzF,OAAO,EAAqB,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAoB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAI/F,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,CAAC;AAkBhD,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,uEAAuE;IACvE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,uBAAuB,CAAC,cAAc,CAAC,CAAC;IACvD,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,eAAO,MAAM,eAAe,8PAkBvB,qBAAqB,6CA4XzB,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -43,8 +43,9 @@ var jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
43
43
|
// Owns the `IExpression` value and offers two views of the same AST —
|
|
44
44
|
// Visual (the builder, Phase 3) and Text (DPQL, this phase). The DPQL text
|
|
45
45
|
// editor bridges to the AST via the LSP `dpql/parse` (text → AST on edit)
|
|
46
|
-
// and `dpql/serialize` (AST → text on entering Text mode); a
|
|
47
|
-
//
|
|
46
|
+
// and `dpql/serialize` (AST → text on entering Text mode); a debounced
|
|
47
|
+
// "Preview" box renders the current AST (`dpql/renderExpression`) when that
|
|
48
|
+
// rendering differs from the text the author typed.
|
|
48
49
|
var reqore_1 = require("@qoretechnologies/reqore");
|
|
49
50
|
var react_1 = require("react");
|
|
50
51
|
var dpqlEditor_1 = require("../../dpqlEditor");
|
|
@@ -52,13 +53,22 @@ var builder_1 = require("./builder");
|
|
|
52
53
|
var useExpressions_1 = require("./useExpressions");
|
|
53
54
|
var useRenderExpression_1 = require("./useRenderExpression");
|
|
54
55
|
var PARSE_DEBOUNCE_MS = 300;
|
|
56
|
+
/**
|
|
57
|
+
* How long the Preview waits before catching up with the editor.
|
|
58
|
+
*
|
|
59
|
+
* It mirrors the AST, so without this it re-renders on every keystroke — a box
|
|
60
|
+
* appearing, changing and vanishing under the line being typed. Slightly
|
|
61
|
+
* SLOWER than the parse it follows, so the preview settles once after the text
|
|
62
|
+
* has stopped moving rather than twitching on the way there.
|
|
63
|
+
*/
|
|
64
|
+
var PREVIEW_DEBOUNCE_MS = 400;
|
|
55
65
|
/** Retry cadence for seeding the Text editor while its LSP session opens. */
|
|
56
66
|
var SEED_RETRY_MS = 400;
|
|
57
67
|
var SEED_MAX_TRIES = 20;
|
|
58
68
|
var noop = function () { return undefined; };
|
|
59
69
|
exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
60
70
|
var _b;
|
|
61
|
-
var value = _a.value, onChange = _a.onChange, type = _a.type, returnType = _a.returnType, readOnly = _a.readOnly, expressionsOverride = _a.expressions, expressionsUrl = _a.expressionsUrl, localTemplates = _a.localTemplates, serverHandled = _a.serverHandled, extraActions = _a.extraActions, provider = _a.provider, recordType = _a.recordType, _c = _a.defaultMode, defaultMode = _c === void 0 ? 'visual' : _c, size = _a.size, reorder = _a.reorder;
|
|
71
|
+
var value = _a.value, onChange = _a.onChange, type = _a.type, returnType = _a.returnType, readOnly = _a.readOnly, expressionsOverride = _a.expressions, expressionsUrl = _a.expressionsUrl, localTemplates = _a.localTemplates, serverHandled = _a.serverHandled, extraActions = _a.extraActions, provider = _a.provider, recordType = _a.recordType, _c = _a.defaultMode, defaultMode = _c === void 0 ? 'visual' : _c, size = _a.size, componentOverrides = _a.componentOverrides, reorder = _a.reorder;
|
|
62
72
|
var expressions = (0, useExpressions_1.useExpressions)({
|
|
63
73
|
override: expressionsOverride,
|
|
64
74
|
expressionsUrl: expressionsUrl,
|
|
@@ -72,65 +82,196 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
72
82
|
// Text mode state. `text` is the DPQL string the editor shows; the AST
|
|
73
83
|
// (`value`) stays the source of truth, kept in sync via parse-on-edit.
|
|
74
84
|
var _f = (0, react_1.useState)(''), text = _f[0], setText = _f[1];
|
|
85
|
+
/* The text the current AST was parsed FROM.
|
|
86
|
+
*
|
|
87
|
+
* The preview renders the AST, and the AST only moves on a SUCCESSFUL
|
|
88
|
+
* parse — so while an edit is mid-flight or simply invalid, the AST still
|
|
89
|
+
* describes the last thing that parsed. Showing it then states something
|
|
90
|
+
* the editor does not contain: deleting the `2` from `1 + 2` left
|
|
91
|
+
* "Unexpected end of input" sitting directly above a Preview confidently
|
|
92
|
+
* reading `1 + 2`. */
|
|
93
|
+
var _g = (0, react_1.useState)(''), astText = _g[0], setAstText = _g[1];
|
|
94
|
+
/**
|
|
95
|
+
* The server's answer to "can this expression's result satisfy the type
|
|
96
|
+
* this field declares?". `null` when nothing has been asked, or when the
|
|
97
|
+
* field declares no type worth asking about.
|
|
98
|
+
*/
|
|
99
|
+
var _h = (0, react_1.useState)(null), typeCheck = _h[0], setTypeCheck = _h[1];
|
|
75
100
|
var dpqlRef = (0, react_1.useRef)(null);
|
|
76
101
|
var parseTimer = (0, react_1.useRef)(null);
|
|
77
102
|
var ast = (0, react_1.useMemo)(function () { return value === null || value === void 0 ? void 0 : value.value; }, [value]);
|
|
103
|
+
/**
|
|
104
|
+
* The type the expression's result has to satisfy, sent with every parse.
|
|
105
|
+
*
|
|
106
|
+
* `auto` and `any` are left out deliberately: they accept anything, so the
|
|
107
|
+
* server would answer "compatible" for everything and the round trip would
|
|
108
|
+
* buy nothing. A field offering a CHOICE of return types has no single
|
|
109
|
+
* answer either, so it asks nothing rather than asking about the wrong one.
|
|
110
|
+
*/
|
|
111
|
+
var targetType = (0, react_1.useMemo)(function () {
|
|
112
|
+
var declared = Array.isArray(returnType) ? undefined : (returnType !== null && returnType !== void 0 ? returnType : type);
|
|
113
|
+
return declared && declared !== 'auto' && declared !== 'any' ? declared : undefined;
|
|
114
|
+
}, [returnType, type]);
|
|
115
|
+
/** Read the type analysis off a parse result, or clear it. */
|
|
116
|
+
var readTypeCheck = (0, react_1.useCallback)(function (result) {
|
|
117
|
+
var _a;
|
|
118
|
+
if (!result || result.type_compatible === undefined) {
|
|
119
|
+
setTypeCheck(null);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
setTypeCheck({
|
|
123
|
+
compatible: !!result.type_compatible,
|
|
124
|
+
mayFail: !!result.coercion_may_fail,
|
|
125
|
+
inferred: result.inferred_type,
|
|
126
|
+
target: result.target_type,
|
|
127
|
+
fix: (_a = result.suggested_fix) === null || _a === void 0 ? void 0 : _a.text,
|
|
128
|
+
});
|
|
129
|
+
}, []);
|
|
78
130
|
// Keep a readable rendering of the current expression in sync — the
|
|
79
131
|
// single live mirror of the AST (server `dpql/renderExpression` when
|
|
80
132
|
// reachable, the client-side approximation otherwise).
|
|
133
|
+
//
|
|
134
|
+
// Debounced: it follows the AST, which follows the text, so rendering it
|
|
135
|
+
// eagerly put a box under the cursor that appeared, changed and vanished
|
|
136
|
+
// while the author was still typing.
|
|
81
137
|
(0, react_1.useEffect)(function () {
|
|
82
138
|
var live = true;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
139
|
+
var timer = setTimeout(function () {
|
|
140
|
+
renderRich(ast !== null && ast !== void 0 ? ast : {}, expressions).then(function (r) {
|
|
141
|
+
if (live)
|
|
142
|
+
setPreview({ text: r.text, server: r.server });
|
|
143
|
+
});
|
|
144
|
+
}, PREVIEW_DEBOUNCE_MS);
|
|
87
145
|
return function () {
|
|
88
146
|
live = false;
|
|
147
|
+
clearTimeout(timer);
|
|
89
148
|
};
|
|
90
149
|
}, [renderRich, ast, expressions]);
|
|
91
150
|
// Tracks whether the user has typed since entering Text mode, so a
|
|
92
151
|
// slow seed response can't clobber their input.
|
|
93
152
|
var userTypedRef = (0, react_1.useRef)(false);
|
|
94
|
-
|
|
153
|
+
/* A cleared value must clear the editor with it.
|
|
154
|
+
*
|
|
155
|
+
* "Clear value" empties the option (`handleValueChange(name, undefined)`),
|
|
156
|
+
* but the DPQL text is local state seeded once on entering Text mode, and
|
|
157
|
+
* the seeding effect returns early for an empty AST — it exists to fill the
|
|
158
|
+
* editor, not to empty it. So the value went and the text stayed: the row
|
|
159
|
+
* reported "This field is required" while still showing `1 + 2`, which
|
|
160
|
+
* reads as the clear having silently failed.
|
|
161
|
+
*
|
|
162
|
+
* Only an AST that HAD content and lost it clears the box. A field that
|
|
163
|
+
* simply starts empty is left alone, so this cannot wipe what an author is
|
|
164
|
+
* part-way through typing before their first parse lands.
|
|
165
|
+
*/
|
|
166
|
+
var hadExpression = (0, react_1.useRef)(false);
|
|
95
167
|
(0, react_1.useEffect)(function () {
|
|
96
|
-
|
|
168
|
+
/* GONE, not merely incomplete.
|
|
169
|
+
*
|
|
170
|
+
* The first cut asked whether the AST still had an `exp`, and that is a
|
|
171
|
+
* state the value passes THROUGH while the author types: a half-written
|
|
172
|
+
* expression parses to something without one. So typing `1 + 2` cleared
|
|
173
|
+
* the editor mid-keystroke and the text vanished under the cursor.
|
|
174
|
+
*
|
|
175
|
+
* A clear removes the value itself, so that is what to watch: `value`
|
|
176
|
+
* empty, not an AST that is between shapes. Typing never produces that —
|
|
177
|
+
* `handleDpqlChange` only emits on a SUCCESSFUL parse, so an incomplete
|
|
178
|
+
* expression leaves the previous value in place. */
|
|
179
|
+
if (ast) {
|
|
180
|
+
hadExpression.current = true;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (hadExpression.current) {
|
|
184
|
+
hadExpression.current = false;
|
|
185
|
+
userTypedRef.current = false;
|
|
186
|
+
setText('');
|
|
187
|
+
}
|
|
97
188
|
}, [ast]);
|
|
98
189
|
// Text mode: parse the DPQL into the AST (debounced).
|
|
190
|
+
/* The session may not be attached when the debounce fires — the editor
|
|
191
|
+
mounts on the render that flips the mode, and its LSP session comes up
|
|
192
|
+
after that. `dpqlRef.current?.parse?.()` is optional all the way down, so
|
|
193
|
+
an early call resolves `undefined`, `readTypeCheck` CLEARS the analysis,
|
|
194
|
+
and nothing asks again: the author types once into a fresh Text field and
|
|
195
|
+
is told nothing about the type until they happen to type another
|
|
196
|
+
character.
|
|
197
|
+
|
|
198
|
+
The seeding effect below already retries for this exact reason
|
|
199
|
+
("`serialize` resolves '' until the editor's LSP session is ready"); the
|
|
200
|
+
parse path needed the same care and did not have it. Same budget as
|
|
201
|
+
seeding, so a session that never arrives gives up rather than spinning.
|
|
202
|
+
|
|
203
|
+
Found by CI, not locally: the session is up before the debounce on a
|
|
204
|
+
fast machine, so the story asserting the type message passed here every
|
|
205
|
+
time and failed on the runner. */
|
|
99
206
|
var handleDpqlChange = (0, react_1.useCallback)(function (next) {
|
|
100
207
|
userTypedRef.current = true;
|
|
101
208
|
setText(next);
|
|
102
209
|
if (parseTimer.current)
|
|
103
210
|
clearTimeout(parseTimer.current);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
var
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
211
|
+
var tries = 0;
|
|
212
|
+
var runParse = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
213
|
+
var parse, result;
|
|
214
|
+
var _a;
|
|
215
|
+
return __generator(this, function (_b) {
|
|
216
|
+
switch (_b.label) {
|
|
217
|
+
case 0:
|
|
218
|
+
parse = (_a = dpqlRef.current) === null || _a === void 0 ? void 0 : _a.parse;
|
|
219
|
+
if (!parse) {
|
|
220
|
+
if (++tries < SEED_MAX_TRIES) {
|
|
221
|
+
parseTimer.current = setTimeout(runParse, SEED_RETRY_MS);
|
|
222
|
+
}
|
|
223
|
+
return [2 /*return*/];
|
|
224
|
+
}
|
|
225
|
+
return [4 /*yield*/, parse(next, targetType)];
|
|
110
226
|
case 1:
|
|
111
|
-
result =
|
|
227
|
+
result = _b.sent();
|
|
228
|
+
readTypeCheck(result);
|
|
112
229
|
if ((result === null || result === void 0 ? void 0 : result.success) && result.expression) {
|
|
230
|
+
setAstText(next);
|
|
113
231
|
// `dpql/parse` returns the field-ready `{ is_expression, value }`.
|
|
114
232
|
onChange(result.expression);
|
|
115
233
|
}
|
|
116
234
|
return [2 /*return*/];
|
|
117
235
|
}
|
|
118
236
|
});
|
|
119
|
-
}); }
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
237
|
+
}); };
|
|
238
|
+
parseTimer.current = setTimeout(runParse, PARSE_DEBOUNCE_MS);
|
|
239
|
+
}, [onChange, targetType, readTypeCheck]);
|
|
240
|
+
/** Whether this visit to Text mode has already put the AST in the editor. */
|
|
241
|
+
var seededRef = (0, react_1.useRef)(false);
|
|
242
|
+
/* Seed the Text editor from the AST whenever Text mode becomes active
|
|
243
|
+
* (including a `defaultMode='text'` mount). This must run as an effect: the
|
|
244
|
+
* editor only mounts on the same render that flips the mode (so `dpqlRef`
|
|
245
|
+
* is null inside the click handler), and `serialize` resolves '' until the
|
|
246
|
+
* editor's LSP session is ready — hence the brief retry loop.
|
|
247
|
+
*
|
|
248
|
+
* It waits for an AST, because the AST can arrive AFTER the mode does. A
|
|
249
|
+
* host flips this shell into Text mode on the very render that accepts an
|
|
250
|
+
* expression, and the value it passes on that render is still the TEXT the
|
|
251
|
+
* author typed — the parsed AST only comes back through the form one render
|
|
252
|
+
* later. Keyed on the mode alone, the effect had already had its only turn
|
|
253
|
+
* by then: it found nothing to serialize, returned, and nothing asked
|
|
254
|
+
* again. The author landed in an empty editor with a Preview of their own
|
|
255
|
+
* expression sitting beside it, which reads as the text having been thrown
|
|
256
|
+
* away.
|
|
257
|
+
*
|
|
258
|
+
* Seeding happens at most ONCE per visit, and that is what makes watching
|
|
259
|
+
* the AST safe: `userTypedRef` guards what is being typed, but the AST
|
|
260
|
+
* MOVES as the author types — every successful parse replaces it — so an
|
|
261
|
+
* effect that re-seeded on each new AST would fight them for the editor. */
|
|
127
262
|
(0, react_1.useEffect)(function () {
|
|
128
|
-
if (mode !== 'text')
|
|
263
|
+
if (mode !== 'text') {
|
|
264
|
+
// Leaving Text asks the question again on the next visit; the typing
|
|
265
|
+
// flag goes with it, or a stale one would suppress that visit's seed.
|
|
266
|
+
seededRef.current = false;
|
|
267
|
+
userTypedRef.current = false;
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
if (seededRef.current || userTypedRef.current)
|
|
129
271
|
return undefined;
|
|
130
|
-
|
|
131
|
-
if (!(seedAst === null || seedAst === void 0 ? void 0 : seedAst.exp))
|
|
272
|
+
if (!(ast === null || ast === void 0 ? void 0 : ast.exp))
|
|
132
273
|
return undefined;
|
|
133
|
-
|
|
274
|
+
var seedAst = ast;
|
|
134
275
|
var cancelled = false;
|
|
135
276
|
var timer = null;
|
|
136
277
|
var tries = 0;
|
|
@@ -145,7 +286,9 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
145
286
|
if (cancelled || userTypedRef.current)
|
|
146
287
|
return [2 /*return*/];
|
|
147
288
|
if (t) {
|
|
289
|
+
seededRef.current = true;
|
|
148
290
|
setText(t);
|
|
291
|
+
setAstText(t);
|
|
149
292
|
}
|
|
150
293
|
else if (++tries < SEED_MAX_TRIES) {
|
|
151
294
|
timer = setTimeout(seed, SEED_RETRY_MS);
|
|
@@ -160,7 +303,7 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
160
303
|
if (timer)
|
|
161
304
|
clearTimeout(timer);
|
|
162
305
|
};
|
|
163
|
-
}, [mode]);
|
|
306
|
+
}, [mode, ast]);
|
|
164
307
|
// Switch to Text: the seeding effect above serializes the AST once
|
|
165
308
|
// the editor's session is up.
|
|
166
309
|
var enterTextMode = (0, react_1.useCallback)(function () {
|
|
@@ -176,9 +319,10 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
176
319
|
if (parseTimer.current)
|
|
177
320
|
clearTimeout(parseTimer.current);
|
|
178
321
|
if (!(mode === 'text' && text)) return [3 /*break*/, 2];
|
|
179
|
-
return [4 /*yield*/, ((_b = (_a = dpqlRef.current) === null || _a === void 0 ? void 0 : _a.parse) === null || _b === void 0 ? void 0 : _b.call(_a, text))];
|
|
322
|
+
return [4 /*yield*/, ((_b = (_a = dpqlRef.current) === null || _a === void 0 ? void 0 : _a.parse) === null || _b === void 0 ? void 0 : _b.call(_a, text, targetType))];
|
|
180
323
|
case 1:
|
|
181
324
|
result = _c.sent();
|
|
325
|
+
readTypeCheck(result);
|
|
182
326
|
if ((result === null || result === void 0 ? void 0 : result.success) && result.expression) {
|
|
183
327
|
onChange(result.expression);
|
|
184
328
|
}
|
|
@@ -188,12 +332,16 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
188
332
|
return [2 /*return*/];
|
|
189
333
|
}
|
|
190
334
|
});
|
|
191
|
-
}); }, [mode, text, onChange]);
|
|
335
|
+
}); }, [mode, text, onChange, targetType, readTypeCheck]);
|
|
192
336
|
(0, react_1.useEffect)(function () { return function () {
|
|
193
337
|
if (parseTimer.current)
|
|
194
338
|
clearTimeout(parseTimer.current);
|
|
195
339
|
}; }, []);
|
|
196
|
-
return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { vertical: true, fluid: true, gapSize: 'small', className: 'expression-field', children: [(0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { gapSize: 'small', fluid: true, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { icon: 'NodeTree', active: mode === 'visual', onClick: enterVisualMode, disabled: readOnly, size: size, children: "Visual" }), (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { icon: 'CodeLine', active: mode === 'text', onClick: enterTextMode, disabled: readOnly, size: size, children: "Text" })] }), mode === 'text' ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(dpqlEditor_1.DpqlEditor, { ref: dpqlRef, value: text, onChange: handleDpqlChange, provider: provider, recordType: recordType, readOnly: readOnly, height: '48px' }),
|
|
340
|
+
return ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { vertical: true, fluid: true, gapSize: 'small', className: 'expression-field', children: [(0, jsx_runtime_1.jsxs)(reqore_1.ReqoreControlGroup, { gapSize: 'small', fluid: true, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { icon: 'NodeTree', active: mode === 'visual', onClick: enterVisualMode, disabled: readOnly, size: size, children: "Visual" }), (0, jsx_runtime_1.jsx)(reqore_1.ReqoreButton, { icon: 'CodeLine', active: mode === 'text', onClick: enterTextMode, disabled: readOnly, size: size, children: "Text" })] }), mode === 'text' ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(dpqlEditor_1.DpqlEditor, { ref: dpqlRef, value: text, onChange: handleDpqlChange, provider: provider, recordType: recordType, readOnly: readOnly, height: '48px' }), typeCheck && !typeCheck.compatible && ((0, jsx_runtime_1.jsxs)(reqore_1.ReqoreMessage, { intent: typeCheck.mayFail ? 'warning' : 'danger', size: 'small', flat: true, opaque: false, title: typeCheck.mayFail ? 'This may not fit' : 'This does not fit', children: ["The expression returns ".concat(typeCheck.inferred, ", and this field holds ").concat(typeCheck.target, "."), typeCheck.mayFail ?
|
|
341
|
+
' That conversion is attempted rather than guaranteed, so the value itself is checked when it runs.'
|
|
342
|
+
: '', typeCheck.fix ? ((0, jsx_runtime_1.jsx)("div", { style: { marginTop: '6px' }, children: (0, jsx_runtime_1.jsx)("code", { "data-testid": 'expression-type-fix', children: typeCheck.fix }) })) : null] })), preview.text &&
|
|
343
|
+
astText.trim() === text.trim() &&
|
|
344
|
+
preview.text.trim() !== text.trim() ? ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMessage, { intent: 'info', size: 'small', title: 'Preview', flat: true, opaque: false, children: preview.server ? (
|
|
197
345
|
// Server rendering shown through a read-only DpqlEditor:
|
|
198
346
|
// template-ref chips + LSP token colours; diagnostics off
|
|
199
347
|
// (the rendering is readable text, not parseable DPQL).
|
|
@@ -201,7 +349,7 @@ exports.ExpressionField = (0, react_1.memo)(function (_a) {
|
|
|
201
349
|
// Default to `auto` when the field declares no return type, so a
|
|
202
350
|
// valid expression renders `muted` rather than `danger` (the
|
|
203
351
|
// builder's intent requires a truthy returnType).
|
|
204
|
-
returnType: ((_b = returnType !== null && returnType !== void 0 ? returnType : type) !== null && _b !== void 0 ? _b : 'auto'), readOnly: readOnly, localTemplates: localTemplates !== null && localTemplates !== void 0 ? localTemplates : { items: [] }, serverHandled: serverHandled, extraActions: extraActions, size: size, reorder: reorder }))] }));
|
|
352
|
+
returnType: ((_b = returnType !== null && returnType !== void 0 ? returnType : type) !== null && _b !== void 0 ? _b : 'auto'), readOnly: readOnly, localTemplates: localTemplates !== null && localTemplates !== void 0 ? localTemplates : { items: [] }, serverHandled: serverHandled, componentOverrides: componentOverrides, extraActions: extraActions, size: size, reorder: reorder }))] }));
|
|
205
353
|
});
|
|
206
354
|
exports.default = exports.ExpressionField;
|
|
207
355
|
//# sourceMappingURL=ExpressionField.js.map
|