@foundationui/smart-input
Advanced tools
Comparing version 1.0.7 to 1.0.8
@@ -1,4 +0,23 @@ | ||
import React from 'react'; | ||
import React, { MutableRefObject } from 'react'; | ||
import { BaseEditor } from 'slate'; | ||
import { ReactEditor } from 'slate-react'; | ||
export interface SmartInputProps { | ||
value: string; | ||
onChange: (newValue: string) => void; | ||
renderText: (props: any) => React.ReactElement; | ||
renderSuggestion: (props: any) => React.ReactElement; | ||
renderPlaceholder?: (props: any) => React.ReactElement; | ||
modelId?: string; | ||
context?: any; | ||
editorRef?: MutableRefObject<any>; | ||
multiline?: boolean; | ||
onBlur?: React.FocusEventHandler<HTMLDivElement>; | ||
onFocus?: React.FocusEventHandler<HTMLDivElement>; | ||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>; | ||
placeholder?: string; | ||
style?: React.CSSProperties; | ||
className?: string; | ||
mockSuggestion?: (text: string) => string; | ||
} | ||
export declare function SmartInput(props: SmartInputProps): JSX.Element; | ||
type CustomElement = { | ||
@@ -23,18 +42,2 @@ type: 'text' | 'completion' | undefined; | ||
} | ||
export interface SmartInputProps { | ||
modelId: string; | ||
value: string; | ||
onChange: (newValue: string) => void; | ||
renderText: (props: any) => React.ReactElement; | ||
renderSuggestion: (props: any) => React.ReactElement; | ||
className?: string; | ||
multiline?: boolean; | ||
onBlur?: React.FocusEventHandler<HTMLDivElement>; | ||
onFocus?: React.FocusEventHandler<HTMLDivElement>; | ||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>; | ||
placeholder?: string; | ||
style?: React.CSSProperties; | ||
apiEndpoint?: string; | ||
} | ||
export declare function SmartInput(props: SmartInputProps): JSX.Element; | ||
export {}; |
@@ -13,15 +13,61 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SmartInput = void 0; | ||
var react_1 = __importDefault(require("react")); | ||
var react_1 = __importStar(require("react")); | ||
var slate_1 = require("slate"); | ||
var slate_history_1 = require("slate-history"); | ||
var slate_react_1 = require("slate-react"); | ||
var sessionSid = '' + Date.now(); | ||
var SmartInputFetcher_1 = require("./SmartInputFetcher"); | ||
function SmartInput(props) { | ||
var editor = react_1.default.useMemo(function () { return (0, slate_history_1.withHistory)((0, slate_react_1.withReact)((0, slate_1.createEditor)())); }, []); | ||
var _a = react_1.default.useState(''), completion = _a[0], setCompletion = _a[1]; | ||
var acceptSuggestion = function () { | ||
acceptCompletion(editor, completion); | ||
setCompletion(''); | ||
}; | ||
var addSuggestion = function (completion) { | ||
if (props.value) { | ||
addCompletion(editor, completion); | ||
setCompletion(completion); | ||
} | ||
}; | ||
var clearSuggestion = function () { | ||
removeCompletion(editor); | ||
setCompletion(''); | ||
}; | ||
var fetcher = react_1.default.useState(new SmartInputFetcher_1.SmartInputFetcher(function (text, suggestion) { | ||
addSuggestion(suggestion); | ||
}, props.modelId, props.modelId ? props.mockSuggestion : function () { return ' -- sample suggestion --'; }))[0]; | ||
fetcher.addSuggestion = function (text, suggestion) { | ||
var currText = props.value; | ||
if (suggestion && (text + suggestion).startsWith(currText)) { | ||
addSuggestion(suggestion); | ||
} | ||
else { | ||
console.log('fetcher wanted to add suggestion, but its invalid with the current text'); | ||
} | ||
}; | ||
var initialValue = [ | ||
@@ -33,2 +79,17 @@ { | ||
]; | ||
(0, react_1.useEffect)(function () { | ||
if (props.editorRef) { | ||
props.editorRef.current = __assign(__assign({}, slate_react_1.ReactEditor.toDOMNode(editor, editor)), { focus: function () { | ||
setTimeout(function () { | ||
slate_react_1.ReactEditor.focus(editor); | ||
if (props.value) { | ||
slate_1.Transforms.select(editor, slate_1.Editor.end(editor, [])); | ||
} | ||
else { | ||
slate_1.Transforms.select(editor, slate_1.Editor.start(editor, [])); | ||
} | ||
}, 50); | ||
} }); | ||
} | ||
}); | ||
var renderElement = react_1.default.useCallback(function (p) { | ||
@@ -41,55 +102,15 @@ switch (p.element.type) { | ||
default: | ||
console.log('default element', p); | ||
return react_1.default.createElement(slate_react_1.DefaultElement, __assign({}, p)); | ||
} | ||
}, [props.renderText, props.renderSuggestion]); | ||
var acceptSuggestion = function () { | ||
acceptCompletion(editor, completion); | ||
setCompletion(''); | ||
}; | ||
var addSuggestion = function (completion) { | ||
addCompletion(editor, completion); | ||
setCompletion(completion); | ||
}; | ||
var clearSuggestion = function () { | ||
removeCompletion(editor); | ||
setCompletion(''); | ||
}; | ||
var sendEvents = react_1.default.useMemo(function () { | ||
return debounce(function (text, context) { | ||
fetch(props.apiEndpoint || 'https://api.lydian.company/app/events', { | ||
method: 'POST', | ||
mode: 'cors', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: "Bearer ".concat(props.modelId), | ||
}, | ||
body: JSON.stringify({ | ||
model: props.modelId, | ||
session: sessionSid, | ||
input: text, | ||
context: context, | ||
}), | ||
}) | ||
.then(function (res) { | ||
if (res.status !== 200) { | ||
console.error("Couldn't fetch suggestions"); | ||
return; | ||
} | ||
return res.json(); | ||
}) | ||
.then(function (data) { | ||
var suggestion = data.suggestion; | ||
if (suggestion) { | ||
addSuggestion(suggestion); | ||
} | ||
}) | ||
.catch(function (e) { return console.error(e); }); | ||
}, 200); | ||
}, [props.modelId]); | ||
var renderPlaceholder = react_1.default.useCallback(function (p) { | ||
return props.renderPlaceholder(__assign(__assign(__assign({}, p), (p.attributes || {})), { style: __assign({ position: 'absolute', pointerEvents: 'none', width: '100%', maxWidth: '100%', userSelect: 'none', textDecoration: 'none' }, (p.style || {})) })); | ||
}, [props.renderPlaceholder]); | ||
return (react_1.default.createElement(slate_react_1.Slate, { editor: editor, value: initialValue, onChange: function (value) { | ||
var text = serialize(value.filter(function (n) { return n.type !== 'completion'; })); | ||
sendEvents(text, {}); | ||
fetcher.fetch(text, props.context || {}); | ||
props.onChange(text); | ||
} }, | ||
react_1.default.createElement(slate_react_1.Editable, { placeholder: props.placeholder, className: props.className, style: props.style, onPaste: function (event) { | ||
react_1.default.createElement(slate_react_1.Editable, { placeholder: props.placeholder, className: props.className, style: __assign(__assign({}, (props.style || {})), { whiteSpace: props.multiline ? undefined : 'pre' }), onPaste: function (event) { | ||
clearSuggestion(); | ||
@@ -104,3 +125,21 @@ event.preventDefault(); | ||
} | ||
}, renderElement: renderElement, onBlur: props.onBlur, onFocus: props.onFocus, onKeyDown: function (event) { | ||
}, renderPlaceholder: props.renderPlaceholder ? renderPlaceholder : undefined, renderElement: renderElement, onBlur: function (e) { | ||
clearSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
if (props.onBlur) { | ||
props.onBlur(e); | ||
} | ||
}, onFocus: function (e) { | ||
if (props.onFocus) { | ||
props.onFocus(e); | ||
if (editor.selection == null) { | ||
if (props.value) { | ||
slate_1.Transforms.select(editor, slate_1.Editor.end(editor, [])); | ||
} | ||
else { | ||
slate_1.Transforms.select(editor, slate_1.Editor.start(editor, [])); | ||
} | ||
} | ||
} | ||
}, onKeyDown: function (event) { | ||
var hasCompletion = completion.trim().length > 0; | ||
@@ -110,2 +149,3 @@ if (event.key === 'Tab' && hasCompletion) { | ||
acceptSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
@@ -115,10 +155,13 @@ else if (event.key === 'ArrowRight' && hasCompletion) { | ||
acceptSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
else if (event.key === 'ArrowLeft' && hasCompletion) { | ||
clearSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
else if ((event.key === 'ArrowDown' || event.ke === 'ArrowUp') && hasCompletion) { | ||
else if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && hasCompletion) { | ||
clearSuggestion(); | ||
} | ||
else if (event.key === 'Backspace') { | ||
fetcher.pendingCompletion = null; | ||
clearSuggestion(); | ||
@@ -133,4 +176,11 @@ } | ||
} | ||
else { | ||
clearSuggestion(); | ||
else if (event.key !== 'Shift') { | ||
if (completion && !event.metaKey && completion.startsWith(event.key) && event.key.length === 1) { | ||
updateCompletion(editor, completion.substring(1)); | ||
setCompletion(completion.substring(1)); | ||
} | ||
else { | ||
fetcher.pendingCompletion = null; | ||
clearSuggestion(); | ||
} | ||
} | ||
@@ -186,18 +236,16 @@ if (props.onKeyDown) { | ||
} | ||
function updateCompletion(editor, newCompletion) { | ||
var isActive = isCompletionActive(editor); | ||
if (isActive && newCompletion.length > 0) { | ||
slate_1.Transforms.delete(editor, { | ||
at: { | ||
anchor: { path: [editor.children.length - 1, 0], offset: 0 }, | ||
focus: { path: [editor.children.length - 1, 0], offset: 1 }, | ||
}, | ||
}); | ||
} | ||
} | ||
var serialize = function (nodes) { | ||
return nodes.map(function (n) { return slate_1.Node.string(n); }).join('\n'); | ||
}; | ||
var debounce = function (callback, wait) { | ||
var timeoutId = null; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
window.clearTimeout(timeoutId); | ||
timeoutId = window.setTimeout(function () { | ||
callback.apply(null, args); | ||
}, wait); | ||
}; | ||
}; | ||
//# sourceMappingURL=SmartInput.js.map |
@@ -1,4 +0,23 @@ | ||
import React from 'react'; | ||
import React, { MutableRefObject } from 'react'; | ||
import { BaseEditor } from 'slate'; | ||
import { ReactEditor } from 'slate-react'; | ||
export interface SmartInputProps { | ||
value: string; | ||
onChange: (newValue: string) => void; | ||
renderText: (props: any) => React.ReactElement; | ||
renderSuggestion: (props: any) => React.ReactElement; | ||
renderPlaceholder?: (props: any) => React.ReactElement; | ||
modelId?: string; | ||
context?: any; | ||
editorRef?: MutableRefObject<any>; | ||
multiline?: boolean; | ||
onBlur?: React.FocusEventHandler<HTMLDivElement>; | ||
onFocus?: React.FocusEventHandler<HTMLDivElement>; | ||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>; | ||
placeholder?: string; | ||
style?: React.CSSProperties; | ||
className?: string; | ||
mockSuggestion?: (text: string) => string; | ||
} | ||
export declare function SmartInput(props: SmartInputProps): JSX.Element; | ||
type CustomElement = { | ||
@@ -23,18 +42,2 @@ type: 'text' | 'completion' | undefined; | ||
} | ||
export interface SmartInputProps { | ||
modelId: string; | ||
value: string; | ||
onChange: (newValue: string) => void; | ||
renderText: (props: any) => React.ReactElement; | ||
renderSuggestion: (props: any) => React.ReactElement; | ||
className?: string; | ||
multiline?: boolean; | ||
onBlur?: React.FocusEventHandler<HTMLDivElement>; | ||
onFocus?: React.FocusEventHandler<HTMLDivElement>; | ||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>; | ||
placeholder?: string; | ||
style?: React.CSSProperties; | ||
apiEndpoint?: string; | ||
} | ||
export declare function SmartInput(props: SmartInputProps): JSX.Element; | ||
export {}; |
@@ -12,10 +12,36 @@ var __assign = (this && this.__assign) || function () { | ||
}; | ||
import React from 'react'; | ||
import { createEditor, Node, Transforms } from 'slate'; | ||
import React, { useEffect } from 'react'; | ||
import { createEditor, Editor, Node, Transforms } from 'slate'; | ||
import { withHistory } from 'slate-history'; | ||
import { DefaultElement, Editable, Slate, withReact } from 'slate-react'; | ||
var sessionSid = '' + Date.now(); | ||
import { DefaultElement, Editable, ReactEditor, Slate, withReact } from 'slate-react'; | ||
import { SmartInputFetcher } from './SmartInputFetcher'; | ||
export function SmartInput(props) { | ||
var editor = React.useMemo(function () { return withHistory(withReact(createEditor())); }, []); | ||
var _a = React.useState(''), completion = _a[0], setCompletion = _a[1]; | ||
var acceptSuggestion = function () { | ||
acceptCompletion(editor, completion); | ||
setCompletion(''); | ||
}; | ||
var addSuggestion = function (completion) { | ||
if (props.value) { | ||
addCompletion(editor, completion); | ||
setCompletion(completion); | ||
} | ||
}; | ||
var clearSuggestion = function () { | ||
removeCompletion(editor); | ||
setCompletion(''); | ||
}; | ||
var fetcher = React.useState(new SmartInputFetcher(function (text, suggestion) { | ||
addSuggestion(suggestion); | ||
}, props.modelId, props.modelId ? props.mockSuggestion : function () { return ' -- sample suggestion --'; }))[0]; | ||
fetcher.addSuggestion = function (text, suggestion) { | ||
var currText = props.value; | ||
if (suggestion && (text + suggestion).startsWith(currText)) { | ||
addSuggestion(suggestion); | ||
} | ||
else { | ||
console.log('fetcher wanted to add suggestion, but its invalid with the current text'); | ||
} | ||
}; | ||
var initialValue = [ | ||
@@ -27,2 +53,17 @@ { | ||
]; | ||
useEffect(function () { | ||
if (props.editorRef) { | ||
props.editorRef.current = __assign(__assign({}, ReactEditor.toDOMNode(editor, editor)), { focus: function () { | ||
setTimeout(function () { | ||
ReactEditor.focus(editor); | ||
if (props.value) { | ||
Transforms.select(editor, Editor.end(editor, [])); | ||
} | ||
else { | ||
Transforms.select(editor, Editor.start(editor, [])); | ||
} | ||
}, 50); | ||
} }); | ||
} | ||
}); | ||
var renderElement = React.useCallback(function (p) { | ||
@@ -35,55 +76,15 @@ switch (p.element.type) { | ||
default: | ||
console.log('default element', p); | ||
return React.createElement(DefaultElement, __assign({}, p)); | ||
} | ||
}, [props.renderText, props.renderSuggestion]); | ||
var acceptSuggestion = function () { | ||
acceptCompletion(editor, completion); | ||
setCompletion(''); | ||
}; | ||
var addSuggestion = function (completion) { | ||
addCompletion(editor, completion); | ||
setCompletion(completion); | ||
}; | ||
var clearSuggestion = function () { | ||
removeCompletion(editor); | ||
setCompletion(''); | ||
}; | ||
var sendEvents = React.useMemo(function () { | ||
return debounce(function (text, context) { | ||
fetch(props.apiEndpoint || 'https://api.lydian.company/app/events', { | ||
method: 'POST', | ||
mode: 'cors', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: "Bearer ".concat(props.modelId), | ||
}, | ||
body: JSON.stringify({ | ||
model: props.modelId, | ||
session: sessionSid, | ||
input: text, | ||
context: context, | ||
}), | ||
}) | ||
.then(function (res) { | ||
if (res.status !== 200) { | ||
console.error("Couldn't fetch suggestions"); | ||
return; | ||
} | ||
return res.json(); | ||
}) | ||
.then(function (data) { | ||
var suggestion = data.suggestion; | ||
if (suggestion) { | ||
addSuggestion(suggestion); | ||
} | ||
}) | ||
.catch(function (e) { return console.error(e); }); | ||
}, 200); | ||
}, [props.modelId]); | ||
var renderPlaceholder = React.useCallback(function (p) { | ||
return props.renderPlaceholder(__assign(__assign(__assign({}, p), (p.attributes || {})), { style: __assign({ position: 'absolute', pointerEvents: 'none', width: '100%', maxWidth: '100%', userSelect: 'none', textDecoration: 'none' }, (p.style || {})) })); | ||
}, [props.renderPlaceholder]); | ||
return (React.createElement(Slate, { editor: editor, value: initialValue, onChange: function (value) { | ||
var text = serialize(value.filter(function (n) { return n.type !== 'completion'; })); | ||
sendEvents(text, {}); | ||
fetcher.fetch(text, props.context || {}); | ||
props.onChange(text); | ||
} }, | ||
React.createElement(Editable, { placeholder: props.placeholder, className: props.className, style: props.style, onPaste: function (event) { | ||
React.createElement(Editable, { placeholder: props.placeholder, className: props.className, style: __assign(__assign({}, (props.style || {})), { whiteSpace: props.multiline ? undefined : 'pre' }), onPaste: function (event) { | ||
clearSuggestion(); | ||
@@ -98,3 +99,21 @@ event.preventDefault(); | ||
} | ||
}, renderElement: renderElement, onBlur: props.onBlur, onFocus: props.onFocus, onKeyDown: function (event) { | ||
}, renderPlaceholder: props.renderPlaceholder ? renderPlaceholder : undefined, renderElement: renderElement, onBlur: function (e) { | ||
clearSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
if (props.onBlur) { | ||
props.onBlur(e); | ||
} | ||
}, onFocus: function (e) { | ||
if (props.onFocus) { | ||
props.onFocus(e); | ||
if (editor.selection == null) { | ||
if (props.value) { | ||
Transforms.select(editor, Editor.end(editor, [])); | ||
} | ||
else { | ||
Transforms.select(editor, Editor.start(editor, [])); | ||
} | ||
} | ||
} | ||
}, onKeyDown: function (event) { | ||
var hasCompletion = completion.trim().length > 0; | ||
@@ -104,2 +123,3 @@ if (event.key === 'Tab' && hasCompletion) { | ||
acceptSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
@@ -109,10 +129,13 @@ else if (event.key === 'ArrowRight' && hasCompletion) { | ||
acceptSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
else if (event.key === 'ArrowLeft' && hasCompletion) { | ||
clearSuggestion(); | ||
fetcher.pendingCompletion = null; | ||
} | ||
else if ((event.key === 'ArrowDown' || event.ke === 'ArrowUp') && hasCompletion) { | ||
else if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && hasCompletion) { | ||
clearSuggestion(); | ||
} | ||
else if (event.key === 'Backspace') { | ||
fetcher.pendingCompletion = null; | ||
clearSuggestion(); | ||
@@ -127,4 +150,11 @@ } | ||
} | ||
else { | ||
clearSuggestion(); | ||
else if (event.key !== 'Shift') { | ||
if (completion && !event.metaKey && completion.startsWith(event.key) && event.key.length === 1) { | ||
updateCompletion(editor, completion.substring(1)); | ||
setCompletion(completion.substring(1)); | ||
} | ||
else { | ||
fetcher.pendingCompletion = null; | ||
clearSuggestion(); | ||
} | ||
} | ||
@@ -179,18 +209,16 @@ if (props.onKeyDown) { | ||
} | ||
function updateCompletion(editor, newCompletion) { | ||
var isActive = isCompletionActive(editor); | ||
if (isActive && newCompletion.length > 0) { | ||
Transforms.delete(editor, { | ||
at: { | ||
anchor: { path: [editor.children.length - 1, 0], offset: 0 }, | ||
focus: { path: [editor.children.length - 1, 0], offset: 1 }, | ||
}, | ||
}); | ||
} | ||
} | ||
var serialize = function (nodes) { | ||
return nodes.map(function (n) { return Node.string(n); }).join('\n'); | ||
}; | ||
var debounce = function (callback, wait) { | ||
var timeoutId = null; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
window.clearTimeout(timeoutId); | ||
timeoutId = window.setTimeout(function () { | ||
callback.apply(null, args); | ||
}, wait); | ||
}; | ||
}; | ||
//# sourceMappingURL=SmartInput.js.map |
{ | ||
"name": "@foundationui/smart-input", | ||
"version": "1.0.7", | ||
"version": "1.0.8", | ||
"description": "Smart input/textarea component for React. Learns to provide inline, tab-completeable suggestions.", | ||
@@ -5,0 +5,0 @@ "main": "lib/cjs/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
59362
21
770