Skip to content

Commit 15be9db

Browse files
committed
chore: Adds internal style api v2 to prompt input
1 parent 92ef62f commit 15be9db

7 files changed

Lines changed: 522 additions & 18 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState } from 'react';
4+
import clsx from 'clsx';
5+
6+
import { PromptInput, PromptInputProps, SpaceBetween } from '~components';
7+
8+
import { SimplePage } from '../app/templates';
9+
10+
import styles from './style-api-v2.scss';
11+
12+
const menus: PromptInputProps.MenuDefinition[] = [
13+
{
14+
id: 'mentions',
15+
trigger: '@',
16+
filteringType: 'auto',
17+
options: [
18+
{ value: 'user-1', label: 'Alice' },
19+
{ value: 'user-2', label: 'Bob' },
20+
{ value: 'user-3', label: 'Charlie', disabled: true },
21+
],
22+
},
23+
];
24+
25+
const referenceTokens: PromptInputProps.InputToken[] = [
26+
{ type: 'text', value: 'Ask ' },
27+
{ type: 'reference', id: 'ref-1', label: 'Alice', value: 'user-1', menuId: 'mentions' },
28+
{ type: 'text', value: ' about the release' },
29+
];
30+
31+
// Reference token ids become element ids, so the second instance on the page needs its own.
32+
const disabledReferenceTokens: PromptInputProps.InputToken[] = [
33+
{ type: 'text', value: 'Ask ' },
34+
{ type: 'reference', id: 'ref-2', label: 'Alice', value: 'user-1', menuId: 'mentions' },
35+
{ type: 'text', value: ' about the release' },
36+
];
37+
38+
// Owned states (invalid, warning, disabled, read-only) are passed as a different class; the interaction states
39+
// (:hover, :focus-within on the root, :hover, :focus on the text element) are handled by selectors in the stylesheet.
40+
function getStyleClassNames({ invalid, warning, disabled, readOnly }: PromptInputProps) {
41+
const state = invalid ? 'invalid' : warning ? 'warning' : disabled ? 'disabled' : readOnly ? 'readonly' : null;
42+
return {
43+
root: state ? styles[`root-${state}`] : styles.root,
44+
textarea: clsx({
45+
[styles['textarea-disabled']]: disabled,
46+
[styles['textarea-readonly']]: readOnly && !disabled,
47+
[styles.textarea]: !disabled && !readOnly,
48+
}),
49+
actionButton: styles['action-button'],
50+
token: disabled ? styles['token-disabled'] : styles.token,
51+
menu: styles.menu,
52+
};
53+
}
54+
55+
function StyledPromptInput({ value: initialValue = '', ...rest }: Omit<PromptInputProps, 'tokens' | 'menus'>) {
56+
const [value, setValue] = useState(initialValue);
57+
return (
58+
<PromptInput
59+
actionButtonAriaLabel="Send"
60+
{...rest}
61+
value={value}
62+
onChange={({ detail }) => setValue(detail.value)}
63+
{...{ styleClassNames: getStyleClassNames(rest) }}
64+
/>
65+
);
66+
}
67+
68+
function StyledTokenPromptInput({ tokens: initialTokens = [], ...rest }: Omit<PromptInputProps, 'value' | 'menus'>) {
69+
const [tokens, setTokens] = useState<readonly PromptInputProps.InputToken[]>(initialTokens);
70+
return (
71+
<PromptInput
72+
actionButtonAriaLabel="Send"
73+
{...rest}
74+
menus={menus}
75+
tokens={tokens}
76+
onChange={({ detail }) => setTokens(detail.tokens ?? [])}
77+
{...{ styleClassNames: getStyleClassNames(rest) }}
78+
/>
79+
);
80+
}
81+
82+
export default function () {
83+
return (
84+
<SimplePage title="Prompt input - Style API v2" screenshotArea={{ disableAnimations: true }} i18n={{}}>
85+
<SpaceBetween size="m" direction="horizontal">
86+
<StyledPromptInput ariaLabel="Default prompt" value="Summarize the release notes" />
87+
<StyledPromptInput ariaLabel="Prompt with placeholder" placeholder="Ask a question" />
88+
<StyledPromptInput
89+
ariaLabel="Prompt with action button"
90+
value="Summarize the release notes"
91+
actionButtonIconName="send"
92+
/>
93+
</SpaceBetween>
94+
<SpaceBetween size="m" direction="horizontal">
95+
<StyledPromptInput ariaLabel="Invalid prompt" value="Too short" invalid={true} actionButtonIconName="send" />
96+
<StyledPromptInput
97+
ariaLabel="Warning prompt"
98+
value="Almost at the limit"
99+
warning={true}
100+
actionButtonIconName="send"
101+
/>
102+
<StyledPromptInput
103+
ariaLabel="Read-only prompt"
104+
value="Read-only text"
105+
readOnly={true}
106+
actionButtonIconName="send"
107+
/>
108+
</SpaceBetween>
109+
<SpaceBetween size="m" direction="horizontal">
110+
<StyledPromptInput
111+
ariaLabel="Disabled prompt"
112+
value="Cannot be edited"
113+
disabled={true}
114+
actionButtonIconName="send"
115+
/>
116+
<StyledPromptInput ariaLabel="Disabled prompt with placeholder" placeholder="Ask a question" disabled={true} />
117+
</SpaceBetween>
118+
<SpaceBetween size="m" direction="horizontal">
119+
<StyledTokenPromptInput
120+
ariaLabel="Prompt with references"
121+
tokens={referenceTokens}
122+
actionButtonIconName="send"
123+
/>
124+
<StyledTokenPromptInput
125+
ariaLabel="Disabled prompt with references"
126+
tokens={disabledReferenceTokens}
127+
disabled={true}
128+
/>
129+
<StyledTokenPromptInput ariaLabel="Token mode prompt with placeholder" placeholder="Ask a question" />
130+
<StyledTokenPromptInput
131+
ariaLabel="Disabled token mode prompt with placeholder"
132+
placeholder="Ask a question"
133+
disabled={true}
134+
/>
135+
</SpaceBetween>
136+
</SimplePage>
137+
);
138+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
@use '~components/styling/prompt-input';
7+
@use '~components/styling/button';
8+
@use '~components/styling/token';
9+
@use '~components/styling/dropdown';
10+
@use '../style-api/palette' as palette;
11+
12+
// Field box. One class per owned state (invalid, warning, disabled, read-only); `:hover` and `:focus-within`
13+
// for the interaction states.
14+
@mixin field {
15+
@include prompt-input.root(
16+
$background: palette.$surface,
17+
$border-color: palette.$border,
18+
$border-width: 2px,
19+
$border-radius: 12px,
20+
$box-shadow: 0 2px 4px palette.$subtle
21+
);
22+
}
23+
24+
.root {
25+
@include field;
26+
27+
&:hover {
28+
@include prompt-input.root($background: palette.$surface-alt, $border-color: palette.$border-accent);
29+
}
30+
31+
&:focus-within {
32+
@include prompt-input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
33+
}
34+
}
35+
36+
.root-invalid {
37+
@include field;
38+
@include prompt-input.root($background: palette.$error-surface, $border-color: palette.$error);
39+
40+
&:focus-within {
41+
@include prompt-input.root($box-shadow: 0 0 0 3px palette.$error-hover);
42+
}
43+
}
44+
45+
.root-warning {
46+
@include field;
47+
@include prompt-input.root($background: palette.$warning-surface, $border-color: palette.$warning);
48+
49+
&:focus-within {
50+
@include prompt-input.root($box-shadow: 0 0 0 3px palette.$warning);
51+
}
52+
}
53+
54+
.root-disabled {
55+
@include field;
56+
@include prompt-input.root(
57+
$background: palette.$control-bg-disabled,
58+
$border-color: palette.$border-disabled,
59+
$box-shadow: none
60+
);
61+
}
62+
63+
.root-readonly {
64+
@include field;
65+
@include prompt-input.root($background: palette.$surface-alt);
66+
67+
&:focus-within {
68+
@include prompt-input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
69+
}
70+
}
71+
72+
// Text element (native textarea or editable element). `:hover` and `:focus` for the interaction states, a class
73+
// for disabled and read-only.
74+
@mixin text {
75+
@include prompt-input.textarea(
76+
$color: palette.$text,
77+
$padding-block: 10px,
78+
$padding-inline: 16px,
79+
$font-family: monospace,
80+
$font-size: 15px,
81+
$font-weight: 600,
82+
$placeholder-color: palette.$accent,
83+
$placeholder-font-size: 13px,
84+
$placeholder-font-style: normal,
85+
$placeholder-font-weight: 400
86+
);
87+
}
88+
89+
.textarea {
90+
@include text;
91+
92+
&:hover {
93+
@include prompt-input.textarea($color: palette.$accent-hover);
94+
}
95+
96+
&:focus {
97+
@include prompt-input.textarea($color: palette.$accent);
98+
}
99+
}
100+
101+
.textarea-disabled {
102+
@include text;
103+
@include prompt-input.textarea($color: palette.$text-disabled, $placeholder-color: palette.$text-disabled);
104+
}
105+
106+
.textarea-readonly {
107+
@include text;
108+
@include prompt-input.textarea($color: palette.$text, $font-weight: 400);
109+
}
110+
111+
// Action button forwards to Button.root.
112+
.action-button {
113+
@include button.root($color: palette.$accent, $focus-ring-color: palette.$accent);
114+
115+
&:hover {
116+
@include button.root($color: palette.$accent-hover);
117+
}
118+
}
119+
120+
// Reference tokens forward to Token.root.
121+
.token {
122+
@include token.root(
123+
$background: palette.$genai-surface,
124+
$color: palette.$genai-text,
125+
$border-color: palette.$genai,
126+
$border-radius: 6px
127+
);
128+
}
129+
130+
.token-disabled {
131+
@include token.root(
132+
$background: palette.$control-bg-disabled,
133+
$color: palette.$text-disabled,
134+
$border-color: palette.$border-disabled,
135+
$border-radius: 6px
136+
);
137+
}
138+
139+
// Menu forwards to Dropdown.container.
140+
.menu {
141+
@include dropdown.container(
142+
$background: palette.$surface-alt,
143+
$border-radius: 12px,
144+
$box-shadow: 0 4px 12px palette.$subtle-active,
145+
$outline-color: palette.$accent,
146+
$outline-width: 2px
147+
);
148+
}

0 commit comments

Comments
 (0)