Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ const InputPopupBase = kind({
*/
onOpenPopup: PropTypes.func,

onRecording: PropTypes.func,

/**
* Opens the popup.
*
Expand Down Expand Up @@ -257,6 +259,8 @@ const InputPopupBase = kind({
*/
popupType: PropTypes.oneOf(['fullscreen', 'overlay']),

recording: PropTypes.bool,

/**
* Size of the input field.
*
Expand All @@ -266,6 +270,9 @@ const InputPopupBase = kind({
*/
size: PropTypes.oneOf(['small', 'large']),

startRecording: PropTypes.func,
stopRecording: PropTypes.func,

/**
* Subtitle below the title of popup.
*
Expand All @@ -284,6 +291,9 @@ const InputPopupBase = kind({
*/
title: PropTypes.string,

transcriptText: PropTypes.string,
setTranscriptText: PropTypes.func,

/**
* Type of the input.
*
Expand Down Expand Up @@ -369,6 +379,7 @@ const InputPopupBase = kind({
numberInputField,
onBeforeChange,
onClose,
onRecording,
onNumberComplete,
onInputKeyDown,
onShow,
Expand All @@ -377,9 +388,14 @@ const InputPopupBase = kind({
popupAriaLabel,
popupClassName,
popupType,
recording,
size,
subtitle,
startRecording,
stopRecording,
setTranscriptText,
title,
transcriptText,
type,
value,
maxLength,
Expand Down Expand Up @@ -470,7 +486,12 @@ const InputPopupBase = kind({
placeholder={placeholder}
onBeforeChange={onBeforeChange}
onKeyDown={onInputKeyDown}
recording={recording}
setTranscriptText={setTranscriptText}
startRecording={startRecording}
stopRecording={stopRecording}
spotlightId={inputFieldSpotlightId}
transcriptText={transcriptText}
/>
}
</Cell>
Expand Down
35 changes: 30 additions & 5 deletions Input/InputField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PropTypes from 'prop-types';
import compose from 'ramda/src/compose';
import {Fragment} from 'react';

import Button from '../Button';
import $L from '../internal/$L';
import Skinnable from '../Skinnable';
import Tooltip from '../TooltipDecorator/Tooltip';
Expand Down Expand Up @@ -156,7 +157,7 @@ const InputFieldBase = kind({
* @param {Object} event
* @public
*/
onChange: PropTypes.func,
onChangeValue: PropTypes.func,

/**
* Called when clicked.
Expand Down Expand Up @@ -194,6 +195,8 @@ const InputFieldBase = kind({
*/
placeholder: PropTypes.string,

recording: PropTypes.bool,

/**
* Indicates the content's text direction is right-to-left.
*
Expand All @@ -211,6 +214,11 @@ const InputFieldBase = kind({
*/
size: PropTypes.oneOf(['small', 'large']),

startRecording: PropTypes.func,
stopRecording: PropTypes.func,
transcriptText: PropTypes.string,
setTranscriptText: PropTypes.func,

/**
* The type of input.
*
Expand Down Expand Up @@ -259,7 +267,10 @@ const InputFieldBase = kind({
}
}
}),
forwardCustom('onChange', ev => ({value: ev.target.value}))
forwardCustom('onChangeValue', (ev, {setTranscriptText}) => {
setTranscriptText('');
return {value: ev.target.value};
})
)
},

Expand All @@ -280,10 +291,13 @@ const InputFieldBase = kind({
}
},
// ensure we have a value so the internal <input> is always controlled
value: ({value}) => typeof value === 'number' ? value : (value || '')
value: ({value, transcriptText}) => {
value = (value || '') + (transcriptText ? ' '+transcriptText : '');
return (value);
}
},

render: ({css, dir, disabled, iconAfter, iconBefore, invalidTooltip, onChange, placeholder, size, type, value, ...rest}) => {
render: ({css, dir, disabled, iconAfter, iconBefore, invalidTooltip, onChange, onChangeValue, stopRecording, startRecording, setTranscriptText, placeholder, recording, size, type, value, ...rest}) => {
const inputProps = extractInputProps(rest);
const voiceProps = extractVoiceProps(rest);
const isPasswordtel = type === 'passwordtel';
Expand Down Expand Up @@ -323,6 +337,17 @@ const InputFieldBase = kind({
/>
<InputFieldDecoratorIcon position="after" size={size}>{iconAfter}</InputFieldDecoratorIcon>
{invalidTooltip}
<Button
color={recording ? "red" : null}
size="small"
backgroundOpacity="opaque"
onClick={recording? (async() => await stopRecording()) : (async() => {
onChangeValue({value});
await startRecording();
})}
icon="voice"
>
</Button>
</div>
);
}
Expand Down Expand Up @@ -353,7 +378,7 @@ const AnnounceDecorator = Wrapped => function AnnounceDecorator (props) {
const InputFieldDecorator = compose(
Pure,
I18nContextDecorator({rtlProp: 'rtl'}),
Changeable,
Changeable({change: 'onChangeValue'}),
InputFieldSpotlightDecorator,
AnnounceDecorator,
Skinnable
Expand Down
36 changes: 35 additions & 1 deletion samples/sampler/stories/default/Input.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// import {useWhisper} from '@chengsokdara/use-whisper';
import {useWhisper} from '../../../../../use-whisper/src';
import Input, {InputBase, InputPopup, InputPopupBase} from '@enact/sandstone/Input';
import {mergeComponentMetadata} from '@enact/storybook-utils';
import {action} from '@enact/storybook-utils/addons/actions';
import {boolean, range, select, text} from '@enact/storybook-utils/addons/controls';

import {useEffect, useState} from 'react';

Input.displayName = 'Input';
const Config = mergeComponentMetadata('Input', InputPopupBase, InputBase, Input);
const ConfigPopup = mergeComponentMetadata('InputPopup', InputPopupBase, InputPopup);
Expand All @@ -22,6 +26,25 @@ export default {
};

export const _Input = (args) => {
const [transcriptText, setTranscriptText] = useState('');
const {
recording,
speaking,
transcribing,
transcript,
pauseRecording,
startRecording,
stopRecording
} = useWhisper({
apiKey: "AZURE_OPENAI_API_KEY",
// removeSilence: true,
streaming: true,
timeSlice: 10_000,
whisperConfig: {
language: 'ko'
}
});

const propOptions = {
// Actions
onBeforeChange: action('onBeforeChange'),
Expand All @@ -47,6 +70,10 @@ export const _Input = (args) => {
backButtonAriaLabel: args['backButtonAriaLabel']
};

useEffect(() => {
setTranscriptText(transcript.text);
}, [transcript.text]);

// Numeric specific props
if (propOptions.type === 'number' || propOptions.type === 'passwordnumber') {
propOptions.numberInputField = args['numberInputField'];
Expand All @@ -69,7 +96,14 @@ export const _Input = (args) => {

return (
<div>
<Input {...propOptions} />
<Input
{...propOptions}
recording={recording}
setTranscriptText={setTranscriptText}
startRecording={startRecording}
stopRecording={stopRecording}
transcriptText={transcriptText}
/>
</div>
);
};
Expand Down