@1771technologies/lytenyte-pro 2.0.4-dev.2 → 2.0.4-dev.4

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 (175) hide show
  1. package/css/components/expression-editor.css +105 -0
  2. package/dist/components/column-manager/column-manager.d.ts +6 -1
  3. package/dist/components/column-manager/column-manager.js +23 -2
  4. package/dist/components/expressions-editor/(expressions)/expression.play.d.ts +4 -0
  5. package/dist/components/expressions-editor/(expressions)/expression.play.js +69 -0
  6. package/dist/components/expressions-editor/create-completion-provider.d.ts +3 -0
  7. package/dist/components/expressions-editor/create-completion-provider.js +161 -0
  8. package/dist/components/expressions-editor/create-key-down-handler.d.ts +18 -0
  9. package/dist/components/expressions-editor/create-key-down-handler.js +49 -0
  10. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.d.ts +2 -0
  11. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.js +56 -0
  12. package/dist/components/expressions-editor/cursor-position/use-cursor-position.d.ts +5 -0
  13. package/dist/components/expressions-editor/cursor-position/use-cursor-position.js +17 -0
  14. package/dist/components/expressions-editor/expression-editor.d.ts +3 -0
  15. package/dist/components/expressions-editor/expression-editor.js +138 -0
  16. package/dist/components/expressions-editor/get-indentation.d.ts +1 -0
  17. package/dist/components/expressions-editor/get-indentation.js +7 -0
  18. package/dist/components/expressions-editor/handle-completion-key-down.d.ts +2 -0
  19. package/dist/components/expressions-editor/handle-completion-key-down.js +22 -0
  20. package/dist/components/expressions-editor/handle-enter-key.d.ts +4 -0
  21. package/dist/components/expressions-editor/handle-enter-key.js +11 -0
  22. package/dist/components/expressions-editor/handle-manual-trigger.d.ts +2 -0
  23. package/dist/components/expressions-editor/handle-manual-trigger.js +3 -0
  24. package/dist/components/expressions-editor/index.d.ts +17 -0
  25. package/dist/components/expressions-editor/index.js +11 -0
  26. package/dist/components/expressions-editor/intellisence/completion-context.d.ts +16 -0
  27. package/dist/components/expressions-editor/intellisence/completion-context.js +7 -0
  28. package/dist/components/expressions-editor/intellisence/completion-item.d.ts +5 -0
  29. package/dist/components/expressions-editor/intellisence/completion-item.js +11 -0
  30. package/dist/components/expressions-editor/intellisence/completion-list.d.ts +8 -0
  31. package/dist/components/expressions-editor/intellisence/completion-list.js +18 -0
  32. package/dist/components/expressions-editor/intellisence/completion-popover.d.ts +1 -0
  33. package/dist/components/expressions-editor/intellisence/completion-popover.js +30 -0
  34. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.d.ts +2 -0
  35. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.js +6 -0
  36. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.d.ts +2 -0
  37. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.js +12 -0
  38. package/dist/components/expressions-editor/intellisence/use-complete-trigger.d.ts +13 -0
  39. package/dist/components/expressions-editor/intellisence/use-complete-trigger.js +41 -0
  40. package/dist/components/expressions-editor/intellisence/use-completions.d.ts +14 -0
  41. package/dist/components/expressions-editor/intellisence/use-completions.js +78 -0
  42. package/dist/components/expressions-editor/replace-word-at-cursor.d.ts +5 -0
  43. package/dist/components/expressions-editor/replace-word-at-cursor.js +7 -0
  44. package/dist/components/expressions-editor/token-highlighter-default.d.ts +4 -0
  45. package/dist/components/expressions-editor/token-highlighter-default.js +18 -0
  46. package/dist/components/expressions-editor/types.d.ts +47 -0
  47. package/dist/components/expressions-editor/types.js +1 -0
  48. package/dist/components/expressions-editor/use-keyboard-navigation.d.ts +10 -0
  49. package/dist/components/expressions-editor/use-keyboard-navigation.js +16 -0
  50. package/dist/components/pill-manager/container.js +55 -9
  51. package/dist/components/pill-manager/item.js +15 -1
  52. package/dist/components/select-all.js +1 -1
  53. package/dist/components/tree-view/root.d.ts +9 -0
  54. package/dist/components/tree-view/root.js +4 -2
  55. package/dist/data-source-client/hooks/use-flattened-groups.js +2 -2
  56. package/dist/expressions/compiler/compile.d.ts +3 -0
  57. package/dist/expressions/compiler/compile.js +4 -0
  58. package/dist/expressions/compiler/eval-constant.d.ts +2 -0
  59. package/dist/expressions/compiler/eval-constant.js +19 -0
  60. package/dist/expressions/compiler/is-constant.d.ts +2 -0
  61. package/dist/expressions/compiler/is-constant.js +6 -0
  62. package/dist/expressions/compiler/make-constant.d.ts +2 -0
  63. package/dist/expressions/compiler/make-constant.js +11 -0
  64. package/dist/expressions/compiler/optimize.d.ts +3 -0
  65. package/dist/expressions/compiler/optimize.js +47 -0
  66. package/dist/expressions/compiler/value-of.d.ts +2 -0
  67. package/dist/expressions/compiler/value-of.js +11 -0
  68. package/dist/expressions/errors/clamp-offset.d.ts +1 -0
  69. package/dist/expressions/errors/clamp-offset.js +3 -0
  70. package/dist/expressions/errors/expression-error.d.ts +10 -0
  71. package/dist/expressions/errors/expression-error.js +19 -0
  72. package/dist/expressions/errors/format-error.d.ts +3 -0
  73. package/dist/expressions/errors/format-error.js +27 -0
  74. package/dist/expressions/errors/get-line-end.d.ts +1 -0
  75. package/dist/expressions/errors/get-line-end.js +8 -0
  76. package/dist/expressions/errors/get-line-start.d.ts +1 -0
  77. package/dist/expressions/errors/get-line-start.js +8 -0
  78. package/dist/expressions/errors/index.d.ts +7 -0
  79. package/dist/expressions/errors/index.js +6 -0
  80. package/dist/expressions/errors/offset-to-position.d.ts +2 -0
  81. package/dist/expressions/errors/offset-to-position.js +17 -0
  82. package/dist/expressions/errors/types.d.ts +10 -0
  83. package/dist/expressions/errors/types.js +1 -0
  84. package/dist/expressions/evaluator/evaluate-binary.d.ts +4 -0
  85. package/dist/expressions/evaluator/evaluate-binary.js +21 -0
  86. package/dist/expressions/evaluator/evaluate-unary.d.ts +2 -0
  87. package/dist/expressions/evaluator/evaluate-unary.js +10 -0
  88. package/dist/expressions/evaluator/evaluate.d.ts +15 -0
  89. package/dist/expressions/evaluator/evaluate.js +62 -0
  90. package/dist/expressions/index.d.ts +5 -0
  91. package/dist/expressions/index.js +3 -0
  92. package/dist/expressions/lexer/predicates/is-alpha-numeric.d.ts +1 -0
  93. package/dist/expressions/lexer/predicates/is-alpha-numeric.js +5 -0
  94. package/dist/expressions/lexer/predicates/is-alpha.d.ts +1 -0
  95. package/dist/expressions/lexer/predicates/is-alpha.js +3 -0
  96. package/dist/expressions/lexer/predicates/is-binary-digit.d.ts +1 -0
  97. package/dist/expressions/lexer/predicates/is-binary-digit.js +3 -0
  98. package/dist/expressions/lexer/predicates/is-digit.d.ts +1 -0
  99. package/dist/expressions/lexer/predicates/is-digit.js +3 -0
  100. package/dist/expressions/lexer/predicates/is-hex-digit.d.ts +1 -0
  101. package/dist/expressions/lexer/predicates/is-hex-digit.js +4 -0
  102. package/dist/expressions/lexer/predicates/is-octal-digit.d.ts +1 -0
  103. package/dist/expressions/lexer/predicates/is-octal-digit.js +3 -0
  104. package/dist/expressions/lexer/tokenize/consume-digits.d.ts +1 -0
  105. package/dist/expressions/lexer/tokenize/consume-digits.js +5 -0
  106. package/dist/expressions/lexer/tokenize/scan-identifier.d.ts +6 -0
  107. package/dist/expressions/lexer/tokenize/scan-identifier.js +20 -0
  108. package/dist/expressions/lexer/tokenize/scan-number.d.ts +4 -0
  109. package/dist/expressions/lexer/tokenize/scan-number.js +32 -0
  110. package/dist/expressions/lexer/tokenize/scan-operator.d.ts +6 -0
  111. package/dist/expressions/lexer/tokenize/scan-operator.js +71 -0
  112. package/dist/expressions/lexer/tokenize/scan-string-literal.d.ts +4 -0
  113. package/dist/expressions/lexer/tokenize/scan-string-literal.js +22 -0
  114. package/dist/expressions/lexer/tokenize/scan-string.d.ts +4 -0
  115. package/dist/expressions/lexer/tokenize/scan-string.js +98 -0
  116. package/dist/expressions/lexer/tokenize/scan-template-literal.d.ts +4 -0
  117. package/dist/expressions/lexer/tokenize/scan-template-literal.js +51 -0
  118. package/dist/expressions/lexer/tokenize/tokenize.d.ts +4 -0
  119. package/dist/expressions/lexer/tokenize/tokenize.js +99 -0
  120. package/dist/expressions/lexer/types.d.ts +12 -0
  121. package/dist/expressions/lexer/types.js +1 -0
  122. package/dist/expressions/parser/parse-array-literal.d.ts +3 -0
  123. package/dist/expressions/parser/parse-array-literal.js +15 -0
  124. package/dist/expressions/parser/parse-expression.d.ts +3 -0
  125. package/dist/expressions/parser/parse-expression.js +74 -0
  126. package/dist/expressions/parser/parse-object-literal.d.ts +3 -0
  127. package/dist/expressions/parser/parse-object-literal.js +69 -0
  128. package/dist/expressions/parser/parse-pipe-transform.d.ts +3 -0
  129. package/dist/expressions/parser/parse-pipe-transform.js +26 -0
  130. package/dist/expressions/parser/parse-postfix.d.ts +3 -0
  131. package/dist/expressions/parser/parse-postfix.js +23 -0
  132. package/dist/expressions/parser/parse-primary.d.ts +3 -0
  133. package/dist/expressions/parser/parse-primary.js +35 -0
  134. package/dist/expressions/parser/parse-template-literal.d.ts +3 -0
  135. package/dist/expressions/parser/parse-template-literal.js +41 -0
  136. package/dist/expressions/parser/parse-unary.d.ts +3 -0
  137. package/dist/expressions/parser/parse-unary.js +28 -0
  138. package/dist/expressions/parser/parse.d.ts +3 -0
  139. package/dist/expressions/parser/parse.js +26 -0
  140. package/dist/expressions/parser/parser-context.d.ts +12 -0
  141. package/dist/expressions/parser/parser-context.js +14 -0
  142. package/dist/expressions/parser/precedence.d.ts +1 -0
  143. package/dist/expressions/parser/precedence.js +8 -0
  144. package/dist/expressions/parser/types.d.ts +97 -0
  145. package/dist/expressions/parser/types.js +1 -0
  146. package/dist/expressions/plugin.d.ts +20 -0
  147. package/dist/expressions/plugin.js +1 -0
  148. package/dist/expressions/plugins/access.d.ts +2 -0
  149. package/dist/expressions/plugins/access.js +138 -0
  150. package/dist/expressions/plugins/arrows.d.ts +2 -0
  151. package/dist/expressions/plugins/arrows.js +83 -0
  152. package/dist/expressions/plugins/booleans.d.ts +2 -0
  153. package/dist/expressions/plugins/booleans.js +34 -0
  154. package/dist/expressions/plugins/collections.d.ts +2 -0
  155. package/dist/expressions/plugins/collections.js +70 -0
  156. package/dist/expressions/plugins/comparison.d.ts +2 -0
  157. package/dist/expressions/plugins/comparison.js +51 -0
  158. package/dist/expressions/plugins/logical.d.ts +2 -0
  159. package/dist/expressions/plugins/logical.js +103 -0
  160. package/dist/expressions/plugins/membership.d.ts +2 -0
  161. package/dist/expressions/plugins/membership.js +71 -0
  162. package/dist/expressions/plugins/pipe.d.ts +2 -0
  163. package/dist/expressions/plugins/pipe.js +57 -0
  164. package/dist/expressions/plugins/resolved-identifier.d.ts +5 -0
  165. package/dist/expressions/plugins/resolved-identifier.js +19 -0
  166. package/dist/expressions/plugins/standard.d.ts +13 -0
  167. package/dist/expressions/plugins/standard.js +23 -0
  168. package/dist/expressions/plugins/strings.d.ts +2 -0
  169. package/dist/expressions/plugins/strings.js +44 -0
  170. package/dist/expressions/plugins/ternary.d.ts +2 -0
  171. package/dist/expressions/plugins/ternary.js +58 -0
  172. package/dist/expressions.d.ts +2 -0
  173. package/dist/expressions.js +2 -0
  174. package/dist/index.d.ts +1 -0
  175. package/package.json +11 -5
@@ -0,0 +1,138 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useCallback, useMemo, Fragment } from "react";
3
+ import { useCompletions } from "./intellisence/use-completions.js";
4
+ import { useCompletionTrigger } from "./intellisence/use-complete-trigger.js";
5
+ import { useKeyboardNavigation } from "./use-keyboard-navigation.js";
6
+ import { useCursorPosition } from "./cursor-position/use-cursor-position.js";
7
+ import { createKeyDownHandler } from "./create-key-down-handler.js";
8
+ import { getWordAtCursor } from "./intellisence/get-word-at-cursor.js";
9
+ import { DefaultTokenHighlighter } from "./token-highlighter-default.js";
10
+ import { CompletionListProvider, CompletionPopoverProvider } from "./intellisence/completion-context.js";
11
+ const sharedFontStyle = {
12
+ fontFamily: "inherit",
13
+ fontSize: "inherit",
14
+ fontWeight: "inherit",
15
+ fontStyle: "inherit",
16
+ letterSpacing: "inherit",
17
+ lineHeight: "inherit",
18
+ padding: "0",
19
+ };
20
+ const triggerCharacters = ["."];
21
+ export function ExpressionEditor({ value, onChange: onValueChange, tokenize, highlight: Highlighter = DefaultTokenHighlighter, completionProvider, placeholder, disabled, readOnly, className, style, children, }) {
22
+ // TODO @Lee: we should eventually allow multiline expressions but for now there isn't really a valid
23
+ // use case that requires multiline expressions without also allow variable support.
24
+ const multiline = false;
25
+ const textareaRef = useRef(null);
26
+ const depsRef = useRef(null);
27
+ const tokens = useMemo(() => tokenize(value), [value, tokenize]);
28
+ const completions = useCompletions(completionProvider);
29
+ const cursorPosition = useCursorPosition(textareaRef);
30
+ const navigation = useKeyboardNavigation({
31
+ onValueChange,
32
+ onDismiss: completions.dismiss,
33
+ textareaRef,
34
+ });
35
+ const trigger = useCompletionTrigger({
36
+ triggerCharacters,
37
+ onTrigger: (toks, pos, word) => {
38
+ cursorPosition.update();
39
+ completions.fetchCompletions(toks, pos, word);
40
+ },
41
+ tokenize,
42
+ });
43
+ depsRef.current = {
44
+ isOpen: completions.isOpen,
45
+ selectedItem: completions.selectedItem,
46
+ navigate: completions.navigate,
47
+ dismiss: completions.dismiss,
48
+ acceptCompletion: navigation.acceptCompletion,
49
+ triggerManually: trigger.triggerManually,
50
+ onValueChange,
51
+ multiline,
52
+ updateFilter: completions.updateFilter,
53
+ handleInputChange: trigger.handleInputChange,
54
+ cancel: trigger.cancel,
55
+ };
56
+ const handleKeyDown = useCallback((e) => {
57
+ const deps = depsRef.current;
58
+ const textarea = e.currentTarget;
59
+ const wordAtCursor = getWordAtCursor(textarea.value, textarea.selectionStart);
60
+ const handler = createKeyDownHandler({
61
+ isPopoverOpen: deps.isOpen,
62
+ multiline: deps.multiline,
63
+ selectedItem: deps.selectedItem,
64
+ wordAtCursor,
65
+ onAcceptCompletion: deps.acceptCompletion,
66
+ onNavigate: deps.navigate,
67
+ onDismiss: deps.dismiss,
68
+ onCancelTrigger: deps.cancel,
69
+ onManualTrigger: () => {
70
+ deps.triggerManually(textarea.value, textarea.selectionStart);
71
+ },
72
+ onValueChange: deps.onValueChange,
73
+ onExternalKeyDown: deps.onExternalKeyDown,
74
+ });
75
+ handler(e);
76
+ }, []);
77
+ const handleChange = useCallback((e) => {
78
+ const { value: newValue, selectionStart } = e.currentTarget;
79
+ const deps = depsRef.current;
80
+ deps.onValueChange(newValue);
81
+ const word = getWordAtCursor(newValue, selectionStart);
82
+ if (deps.isOpen) {
83
+ deps.updateFilter(word.word);
84
+ }
85
+ deps.handleInputChange(newValue, selectionStart);
86
+ }, []);
87
+ const handleBlur = useCallback(() => {
88
+ const deps = depsRef.current;
89
+ deps.dismiss();
90
+ deps.cancel();
91
+ }, []);
92
+ const handleItemSelect = useCallback((item) => {
93
+ const textarea = textareaRef.current;
94
+ if (!textarea)
95
+ return;
96
+ const wordAtCursor = getWordAtCursor(textarea.value, textarea.selectionStart);
97
+ depsRef.current.acceptCompletion(item, wordAtCursor);
98
+ }, []);
99
+ const whiteSpace = multiline ? "pre-wrap" : "pre";
100
+ const wordBreak = multiline ? "break-word" : undefined;
101
+ const gridCell = {
102
+ gridArea: "1 / 1",
103
+ ...sharedFontStyle,
104
+ whiteSpace,
105
+ wordBreak,
106
+ overflowWrap: multiline ? "break-word" : undefined,
107
+ boxSizing: "border-box",
108
+ };
109
+ const popoverValue = useMemo(() => {
110
+ return { isOpen: completions.isOpen, coordinates: cursorPosition.coordinates };
111
+ }, [completions.isOpen, cursorPosition.coordinates]);
112
+ const listValue = useMemo(() => {
113
+ return {
114
+ items: completions.filteredItems,
115
+ loading: completions.isLoading,
116
+ selectedIndex: completions.selectedIndex,
117
+ onSelect: handleItemSelect,
118
+ };
119
+ }, [completions.filteredItems, completions.isLoading, completions.selectedIndex, handleItemSelect]);
120
+ return (_jsxs("div", { className: className, style: {
121
+ display: "grid",
122
+ width: "100%",
123
+ ...style,
124
+ }, "data-ln-expression-editor": true, children: [_jsx("textarea", { ref: textareaRef, value: value, onChange: handleChange, onKeyDown: handleKeyDown, onBlur: handleBlur, disabled: disabled, readOnly: readOnly, placeholder: placeholder, rows: multiline ? undefined : 1, "data-ln-expression-input": true, style: {
125
+ ...gridCell,
126
+ display: "block",
127
+ width: "100%",
128
+ resize: multiline ? "vertical" : "none",
129
+ background: "transparent",
130
+ color: "transparent",
131
+ WebkitTextFillColor: "transparent",
132
+ outline: "none",
133
+ overflow: multiline ? "auto" : "hidden",
134
+ margin: 0,
135
+ border: 0,
136
+ zIndex: 1,
137
+ }, autoCapitalize: "off", autoComplete: "off", autoCorrect: "off", spellCheck: false }), _jsx("div", { "aria-hidden": "true", style: { ...gridCell, pointerEvents: "none", overflow: "hidden", zIndex: 0 }, "data-ln-expression--tokens": true, children: tokens.map((token) => (_jsx(Fragment, { children: _jsx(Highlighter, { token: token }) }, token.start))) }), _jsx(CompletionPopoverProvider, { value: popoverValue, children: _jsx(CompletionListProvider, { value: listValue, children: children }) })] }));
138
+ }
@@ -0,0 +1 @@
1
+ export declare function getIndentation(text: string, cursorPosition: number): string;
@@ -0,0 +1,7 @@
1
+ export function getIndentation(text, cursorPosition) {
2
+ const textBeforeCursor = text.substring(0, cursorPosition);
3
+ const lines = textBeforeCursor.split("\n");
4
+ const currentLine = lines[lines.length - 1];
5
+ const match = currentLine.match(/^(\s*)/);
6
+ return match ? match[1] : "";
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { CompletionAction, KeybindingConfig } from "./types.js";
2
+ export declare function handleCompletionKeyDown(key: string, keybindings?: KeybindingConfig): CompletionAction;
@@ -0,0 +1,22 @@
1
+ const DEFAULT_KEYBINDINGS = {
2
+ accept: ["Enter", "Tab"],
3
+ dismiss: ["Escape"],
4
+ navigateUp: ["ArrowUp"],
5
+ navigateDown: ["ArrowDown"],
6
+ };
7
+ export function handleCompletionKeyDown(key, keybindings) {
8
+ const bindings = { ...DEFAULT_KEYBINDINGS, ...keybindings };
9
+ if (bindings.navigateUp.includes(key)) {
10
+ return { type: "navigate", direction: "up" };
11
+ }
12
+ if (bindings.navigateDown.includes(key)) {
13
+ return { type: "navigate", direction: "down" };
14
+ }
15
+ if (bindings.accept.includes(key)) {
16
+ return { type: "accept" };
17
+ }
18
+ if (bindings.dismiss.includes(key)) {
19
+ return { type: "dismiss" };
20
+ }
21
+ return { type: "none" };
22
+ }
@@ -0,0 +1,4 @@
1
+ export declare function handleEnterKey(value: string, selectionStart: number, selectionEnd: number, multiline: boolean): {
2
+ value: string;
3
+ cursorPosition: number;
4
+ } | null;
@@ -0,0 +1,11 @@
1
+ import { getIndentation } from "./get-indentation.js";
2
+ export function handleEnterKey(value, selectionStart, selectionEnd, multiline) {
3
+ if (!multiline) {
4
+ return null;
5
+ }
6
+ const indent = getIndentation(value, selectionStart);
7
+ const insertion = "\n" + indent;
8
+ const newValue = value.substring(0, selectionStart) + insertion + value.substring(selectionEnd);
9
+ const cursorPosition = selectionStart + insertion.length;
10
+ return { value: newValue, cursorPosition };
11
+ }
@@ -0,0 +1,2 @@
1
+ import type { KeyboardEvent } from "react";
2
+ export declare function isManualTrigger(e: KeyboardEvent<HTMLTextAreaElement>): boolean;
@@ -0,0 +1,3 @@
1
+ export function isManualTrigger(e) {
2
+ return (e.ctrlKey || e.metaKey) && e.key === " ";
3
+ }
@@ -0,0 +1,17 @@
1
+ import { ExpressionEditor as Expr } from "./expression-editor.js";
2
+ export declare const ExpressionEditor: {
3
+ Root: typeof Expr;
4
+ CompletionPopover: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
5
+ CompletionList: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "children"> & {
6
+ children?: (props: {
7
+ readonly items: import("./types.js").CompletionItem[];
8
+ readonly loading: boolean;
9
+ }) => import("react").ReactNode;
10
+ }, "ref"> & import("react").RefAttributes<HTMLUListElement>>;
11
+ CompletionListItem: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLLIElement> & import("react").LiHTMLAttributes<HTMLLIElement> & {
12
+ readonly item: import("./types.js").CompletionItem;
13
+ readonly index: number;
14
+ }, "ref"> & import("react").RefAttributes<HTMLLIElement>>;
15
+ };
16
+ export { createCompletionProvider } from "./create-completion-provider.js";
17
+ export type { CompletionItem } from "./types.js";
@@ -0,0 +1,11 @@
1
+ import { ExpressionEditor as Expr } from "./expression-editor.js";
2
+ import { CompletionListItem } from "./intellisence/completion-item.js";
3
+ import { CompletionList } from "./intellisence/completion-list.js";
4
+ import { CompletionPopover } from "./intellisence/completion-popover.js";
5
+ export const ExpressionEditor = {
6
+ Root: Expr,
7
+ CompletionPopover,
8
+ CompletionList,
9
+ CompletionListItem,
10
+ };
11
+ export { createCompletionProvider } from "./create-completion-provider.js";
@@ -0,0 +1,16 @@
1
+ import type { CompletionItem, CursorCoordinates } from "../types";
2
+ interface ContextPopover {
3
+ readonly isOpen: boolean;
4
+ readonly coordinates: CursorCoordinates;
5
+ }
6
+ export declare const CompletionPopoverProvider: import("react").Provider<ContextPopover>;
7
+ export declare const useCompletionPopover: () => ContextPopover;
8
+ interface ContextList {
9
+ readonly items: CompletionItem[];
10
+ readonly loading: boolean;
11
+ readonly selectedIndex: number;
12
+ readonly onSelect: (item: CompletionItem) => void;
13
+ }
14
+ export declare const CompletionListProvider: import("react").Provider<ContextList>;
15
+ export declare const useCompletionListContext: () => ContextList;
16
+ export {};
@@ -0,0 +1,7 @@
1
+ import { createContext, useContext } from "react";
2
+ const context = createContext({});
3
+ export const CompletionPopoverProvider = context.Provider;
4
+ export const useCompletionPopover = () => useContext(context);
5
+ const contextList = createContext({});
6
+ export const CompletionListProvider = contextList.Provider;
7
+ export const useCompletionListContext = () => useContext(contextList);
@@ -0,0 +1,5 @@
1
+ import type { CompletionItem } from "../types.js";
2
+ export declare const CompletionListItem: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLLIElement> & import("react").LiHTMLAttributes<HTMLLIElement> & {
3
+ readonly item: CompletionItem;
4
+ readonly index: number;
5
+ }, "ref"> & import("react").RefAttributes<HTMLLIElement>>;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { useCompletionListContext } from "./completion-context.js";
4
+ function CompleteListItemBase({ item, index, ...props }, ref) {
5
+ const { selectedIndex, onSelect } = useCompletionListContext();
6
+ return (_jsx("li", { ...props, ref: ref, role: "option", "aria-selected": index === selectedIndex, "data-ln-selected": index === selectedIndex ? "" : undefined, onMouseDown: (e) => {
7
+ e.preventDefault();
8
+ onSelect(item);
9
+ }, "data-ln-expression-completion-item": true }));
10
+ }
11
+ export const CompletionListItem = forwardRef(CompleteListItemBase);
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from "react";
2
+ import type { CompletionItem } from "../types.js";
3
+ export declare const CompletionList: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "children"> & {
4
+ children?: (props: {
5
+ readonly items: CompletionItem[];
6
+ readonly loading: boolean;
7
+ }) => ReactNode;
8
+ }, "ref"> & import("react").RefAttributes<HTMLUListElement>>;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef, useEffect, forwardRef } from "react";
3
+ import { useCompletionListContext } from "./completion-context.js";
4
+ import { useCombinedRefs } from "@1771technologies/lytenyte-core/internal";
5
+ function CompletionListBase({ children, ...props }, ref) {
6
+ const { selectedIndex, items, loading } = useCompletionListContext();
7
+ const listRef = useRef(null);
8
+ useEffect(() => {
9
+ const list = listRef.current;
10
+ if (!list)
11
+ return;
12
+ const selected = list.querySelector("[data-ln-selected]");
13
+ selected?.scrollIntoView({ block: "nearest" });
14
+ }, [selectedIndex]);
15
+ const combined = useCombinedRefs(listRef, ref);
16
+ return (_jsx("ul", { ...props, ref: combined, role: "listbox", "data-ln-expression-completion-list": true, children: children?.({ items, loading }) }));
17
+ }
18
+ export const CompletionList = forwardRef(CompletionListBase);
@@ -0,0 +1 @@
1
+ export declare const CompletionPopover: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef, useEffect, forwardRef } from "react";
3
+ import { useCompletionPopover } from "./completion-context.js";
4
+ import { useCombinedRefs } from "@1771technologies/lytenyte-core/internal";
5
+ function CompletionPopoverImpl({ children, ...props }, ref) {
6
+ const { isOpen, coordinates } = useCompletionPopover();
7
+ const popoverRef = useRef(null);
8
+ useEffect(() => {
9
+ const el = popoverRef.current;
10
+ if (!el)
11
+ return;
12
+ if (isOpen) {
13
+ if (!el.matches(":popover-open"))
14
+ el.showPopover();
15
+ }
16
+ else {
17
+ if (el.matches(":popover-open"))
18
+ el.hidePopover();
19
+ }
20
+ }, [isOpen]);
21
+ const combined = useCombinedRefs(ref, popoverRef);
22
+ return (_jsx("div", { ...props, ref: combined, popover: "manual", "data-ln-expression-popover": true, style: {
23
+ position: "fixed",
24
+ top: coordinates.top + coordinates.lineHeight,
25
+ left: coordinates.left,
26
+ margin: 0,
27
+ inset: "unset",
28
+ }, children: isOpen ? children : null }));
29
+ }
30
+ export const CompletionPopover = forwardRef(CompletionPopoverImpl);
@@ -0,0 +1,2 @@
1
+ import type { CompletionItem } from "../types.js";
2
+ export declare function filterCompletionsByPrefix(items: CompletionItem[], prefix: string): CompletionItem[];
@@ -0,0 +1,6 @@
1
+ export function filterCompletionsByPrefix(items, prefix) {
2
+ if (!prefix)
3
+ return items;
4
+ const lower = prefix.toLowerCase();
5
+ return items.filter((item) => item.label.toLowerCase().startsWith(lower));
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { WordAtCursor } from "../types.js";
2
+ export declare function getWordAtCursor(text: string, cursorPosition: number): WordAtCursor;
@@ -0,0 +1,12 @@
1
+ export function getWordAtCursor(text, cursorPosition) {
2
+ const wordPattern = /[a-zA-Z0-9_]/;
3
+ let start = cursorPosition;
4
+ while (start > 0 && wordPattern.test(text[start - 1])) {
5
+ start--;
6
+ }
7
+ return {
8
+ word: text.substring(start, cursorPosition),
9
+ start,
10
+ end: cursorPosition,
11
+ };
12
+ }
@@ -0,0 +1,13 @@
1
+ import type { WordAtCursor } from "../types";
2
+ import type { Token } from "../../../expressions/lexer/types";
3
+ type TriggerConfig = {
4
+ triggerCharacters: string[];
5
+ onTrigger: (tokens: Token[], cursorPosition: number, word: WordAtCursor) => void;
6
+ tokenize: (value: string) => Token[];
7
+ };
8
+ export declare function useCompletionTrigger(config: TriggerConfig): {
9
+ handleInputChange: (value: string, cursorPosition: number) => void;
10
+ triggerManually: (value: string, cursorPosition: number) => void;
11
+ cancel: () => void;
12
+ };
13
+ export {};
@@ -0,0 +1,41 @@
1
+ import { useCallback, useRef, useEffect } from "react";
2
+ import { getWordAtCursor } from "./get-word-at-cursor.js";
3
+ const DEBOUNCE_DELAY = 150;
4
+ export function useCompletionTrigger(config) {
5
+ const timerRef = useRef(null);
6
+ const configRef = useRef(config);
7
+ configRef.current = config;
8
+ const cancel = useCallback(() => {
9
+ if (timerRef.current !== null) {
10
+ clearTimeout(timerRef.current);
11
+ timerRef.current = null;
12
+ }
13
+ }, []);
14
+ useEffect(() => cancel, [cancel]);
15
+ const triggerManually = useCallback((value, cursorPosition) => {
16
+ cancel();
17
+ const { tokenize, onTrigger } = configRef.current;
18
+ const tokens = tokenize(value);
19
+ const word = getWordAtCursor(value, cursorPosition);
20
+ onTrigger(tokens, cursorPosition, word);
21
+ }, [cancel]);
22
+ const handleInputChange = useCallback((value, cursorPosition) => {
23
+ cancel();
24
+ const { triggerCharacters, tokenize, onTrigger } = configRef.current;
25
+ const charBeforeCursor = value[cursorPosition - 1] || "";
26
+ if (triggerCharacters.includes(charBeforeCursor)) {
27
+ const tokens = tokenize(value);
28
+ const word = getWordAtCursor(value, cursorPosition);
29
+ onTrigger(tokens, cursorPosition, word);
30
+ return;
31
+ }
32
+ const word = getWordAtCursor(value, cursorPosition);
33
+ if (word.word.length > 0) {
34
+ timerRef.current = setTimeout(() => {
35
+ const tokens = configRef.current.tokenize(value);
36
+ configRef.current.onTrigger(tokens, cursorPosition, word);
37
+ }, DEBOUNCE_DELAY);
38
+ }
39
+ }, [cancel]);
40
+ return { handleInputChange, triggerManually, cancel };
41
+ }
@@ -0,0 +1,14 @@
1
+ import type { CompletionItem, WordAtCursor } from "../types.js";
2
+ import type { Token } from "../../../expressions/lexer/types.js";
3
+ export declare function useCompletions(completionProvider?: (tokens: Token[], cursorPosition: number) => CompletionItem[] | Promise<CompletionItem[]>): {
4
+ selectedItem: CompletionItem | null;
5
+ fetchCompletions: (tokens: Token[], cursorPosition: number, word: WordAtCursor) => Promise<void>;
6
+ updateFilter: (prefix: string) => void;
7
+ navigate: (direction: "up" | "down") => void;
8
+ dismiss: () => void;
9
+ items: CompletionItem[];
10
+ filteredItems: CompletionItem[];
11
+ selectedIndex: number;
12
+ isOpen: boolean;
13
+ isLoading: boolean;
14
+ };
@@ -0,0 +1,78 @@
1
+ import { useState, useCallback, useRef } from "react";
2
+ import { filterCompletionsByPrefix } from "./filter-completions-by-prefix.js";
3
+ export function useCompletions(completionProvider) {
4
+ const [state, setState] = useState({
5
+ items: [],
6
+ filteredItems: [],
7
+ selectedIndex: 0,
8
+ isOpen: false,
9
+ isLoading: false,
10
+ });
11
+ const requestIdRef = useRef(0);
12
+ const fetchCompletions = useCallback(async (tokens, cursorPosition, word) => {
13
+ if (!completionProvider)
14
+ return;
15
+ const requestId = ++requestIdRef.current;
16
+ setState((prev) => ({ ...prev, isLoading: true, isOpen: true }));
17
+ try {
18
+ const result = await Promise.resolve(completionProvider(tokens, cursorPosition));
19
+ if (requestId !== requestIdRef.current)
20
+ return;
21
+ const filtered = filterCompletionsByPrefix(result, word.word);
22
+ setState({
23
+ items: result,
24
+ filteredItems: filtered,
25
+ selectedIndex: 0,
26
+ isOpen: filtered.length > 0,
27
+ isLoading: false,
28
+ });
29
+ }
30
+ catch {
31
+ if (requestId !== requestIdRef.current)
32
+ return;
33
+ setState((prev) => ({
34
+ ...prev,
35
+ isOpen: false,
36
+ isLoading: false,
37
+ }));
38
+ }
39
+ }, [completionProvider]);
40
+ const updateFilter = useCallback((prefix) => {
41
+ setState((prev) => {
42
+ const filtered = filterCompletionsByPrefix(prev.items, prefix);
43
+ return {
44
+ ...prev,
45
+ filteredItems: filtered,
46
+ selectedIndex: 0,
47
+ isOpen: filtered.length > 0,
48
+ };
49
+ });
50
+ }, []);
51
+ const navigate = useCallback((direction) => {
52
+ setState((prev) => {
53
+ if (prev.filteredItems.length === 0)
54
+ return prev;
55
+ const delta = direction === "down" ? 1 : -1;
56
+ const count = prev.filteredItems.length;
57
+ const nextIndex = (prev.selectedIndex + delta + count) % count;
58
+ return { ...prev, selectedIndex: nextIndex };
59
+ });
60
+ }, []);
61
+ const dismiss = useCallback(() => {
62
+ setState((prev) => ({
63
+ ...prev,
64
+ isOpen: false,
65
+ isLoading: false,
66
+ selectedIndex: 0,
67
+ }));
68
+ }, []);
69
+ const selectedItem = state.isOpen && state.filteredItems.length > 0 ? state.filteredItems[state.selectedIndex] : null;
70
+ return {
71
+ ...state,
72
+ selectedItem,
73
+ fetchCompletions,
74
+ updateFilter,
75
+ navigate,
76
+ dismiss,
77
+ };
78
+ }
@@ -0,0 +1,5 @@
1
+ import type { WordAtCursor } from "./types.js";
2
+ export declare function replaceWordAtCursor(text: string, wordRange: WordAtCursor, replacement: string): {
3
+ value: string;
4
+ cursorPosition: number;
5
+ };
@@ -0,0 +1,7 @@
1
+ export function replaceWordAtCursor(text, wordRange, replacement) {
2
+ const before = text.substring(0, wordRange.start);
3
+ const after = text.substring(wordRange.end);
4
+ const value = before + replacement + after;
5
+ const cursorPosition = wordRange.start + replacement.length;
6
+ return { value, cursorPosition };
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { Token } from "../../expressions/lexer/types.js";
2
+ export declare function DefaultTokenHighlighter({ token }: {
3
+ token: Token;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const tokenToSyntaxColor = {
3
+ Number: "number",
4
+ String: "string",
5
+ TemplateLiteral: "string",
6
+ Spread: "operator",
7
+ Punctuation: "operator",
8
+ Pipe: "operator",
9
+ Operator: "operator",
10
+ Arrow: "operator",
11
+ Identifier: "identifier",
12
+ ExpressionError: "error",
13
+ Unparsed: "punctuation",
14
+ };
15
+ export function DefaultTokenHighlighter({ token }) {
16
+ const expr = tokenToSyntaxColor[token.type];
17
+ return _jsx("span", { style: { color: `var(--ln-expr-${expr})` }, children: token.value });
18
+ }
@@ -0,0 +1,47 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { Token } from "../../expressions/lexer/types.js";
3
+ export type CompletionItem = {
4
+ label: string;
5
+ kind: string;
6
+ id: string;
7
+ };
8
+ export type KeybindingConfig = {
9
+ accept?: string[];
10
+ dismiss?: string[];
11
+ navigateUp?: string[];
12
+ navigateDown?: string[];
13
+ };
14
+ export type CompletionAction = {
15
+ type: "navigate";
16
+ direction: "up" | "down";
17
+ } | {
18
+ type: "accept";
19
+ } | {
20
+ type: "dismiss";
21
+ } | {
22
+ type: "none";
23
+ };
24
+ export type WordAtCursor = {
25
+ word: string;
26
+ start: number;
27
+ end: number;
28
+ };
29
+ export type CursorCoordinates = {
30
+ top: number;
31
+ left: number;
32
+ lineHeight: number;
33
+ };
34
+ export type ExpressionEditorProps = {
35
+ value: string;
36
+ onChange: (value: string) => void;
37
+ tokenize: (value: string) => Token[];
38
+ highlight?: (props: {
39
+ token: Token;
40
+ }) => ReactNode;
41
+ completionProvider?: (tokens: Token[], index: number) => CompletionItem[] | Promise<CompletionItem[]>;
42
+ placeholder?: string;
43
+ disabled?: boolean;
44
+ readOnly?: boolean;
45
+ className?: string;
46
+ style?: CSSProperties;
47
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { CompletionItem, WordAtCursor } from "./types";
2
+ interface NavigationDeps {
3
+ readonly onValueChange: (value: string) => void;
4
+ readonly onDismiss: () => void;
5
+ readonly textareaRef: React.RefObject<HTMLTextAreaElement | null>;
6
+ }
7
+ export declare function useKeyboardNavigation({ onValueChange, onDismiss, textareaRef }: NavigationDeps): {
8
+ acceptCompletion: (item: CompletionItem, word: WordAtCursor) => void;
9
+ };
10
+ export {};
@@ -0,0 +1,16 @@
1
+ import { useCallback } from "react";
2
+ import { replaceWordAtCursor } from "./replace-word-at-cursor.js";
3
+ export function useKeyboardNavigation({ onValueChange, onDismiss, textareaRef }) {
4
+ const acceptCompletion = useCallback((item, word) => {
5
+ const textarea = textareaRef.current;
6
+ if (!textarea)
7
+ return;
8
+ const { value, cursorPosition } = replaceWordAtCursor(textarea.value, word, item.label);
9
+ textarea.value = value;
10
+ textarea.selectionStart = cursorPosition;
11
+ textarea.selectionEnd = cursorPosition;
12
+ onValueChange(value);
13
+ onDismiss();
14
+ }, [onValueChange, onDismiss, textareaRef]);
15
+ return { acceptCompletion };
16
+ }