Skip to content
Draft
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
136 changes: 136 additions & 0 deletions pages/prompt-input/style-api-v2.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import clsx from 'clsx';

import { PromptInput, PromptInputProps, SpaceBetween } from '~components';

import { SimplePage } from '../app/templates';

import styles from './style-api-v2.scss';

const menus: PromptInputProps.MenuDefinition[] = [
{
id: 'mentions',
trigger: '@',
filteringType: 'auto',
options: [
{ value: 'user-1', label: 'Alice' },
{ value: 'user-2', label: 'Bob' },
{ value: 'user-3', label: 'Charlie', disabled: true },
],
},
];

const referenceTokens: PromptInputProps.InputToken[] = [
{ type: 'text', value: 'Ask ' },
{ type: 'reference', id: 'ref-1', label: 'Alice', value: 'user-1', menuId: 'mentions' },
{ type: 'text', value: ' about the release' },
];

// Reference token ids become element ids, so the second instance on the page needs its own.
const disabledReferenceTokens: PromptInputProps.InputToken[] = [
{ type: 'text', value: 'Ask ' },
{ type: 'reference', id: 'ref-2', label: 'Alice', value: 'user-1', menuId: 'mentions' },
{ type: 'text', value: ' about the release' },
];

function getStyleClassNames({ invalid, warning, disabled, readOnly }: PromptInputProps) {
const state = invalid ? 'invalid' : warning ? 'warning' : disabled ? 'disabled' : readOnly ? 'readonly' : null;
return {
root: state ? styles[`root-${state}`] : styles.root,
textarea: clsx({
[styles['textarea-disabled']]: disabled,
[styles['textarea-readonly']]: readOnly && !disabled,
[styles.textarea]: !disabled && !readOnly,
}),
actionButton: styles['action-button'],
token: disabled ? styles['token-disabled'] : styles.token,
menu: styles.menu,
};
}

function StyledPromptInput({ value: initialValue = '', ...rest }: Omit<PromptInputProps, 'tokens' | 'menus'>) {
const [value, setValue] = useState(initialValue);
return (
<PromptInput
actionButtonAriaLabel="Send"
{...rest}
value={value}
onChange={({ detail }) => setValue(detail.value)}
{...{ styleClassNames: getStyleClassNames(rest) }}
/>
);
}

function StyledTokenPromptInput({ tokens: initialTokens = [], ...rest }: Omit<PromptInputProps, 'value' | 'menus'>) {
const [tokens, setTokens] = useState<readonly PromptInputProps.InputToken[]>(initialTokens);
return (
<PromptInput
actionButtonAriaLabel="Send"
{...rest}
menus={menus}
tokens={tokens}
onChange={({ detail }) => setTokens(detail.tokens ?? [])}
{...{ styleClassNames: getStyleClassNames(rest) }}
/>
);
}

export default function () {
return (
<SimplePage title="Prompt input - Style API v2" screenshotArea={{ disableAnimations: true }} i18n={{}}>
<SpaceBetween size="m" direction="horizontal">
<StyledPromptInput ariaLabel="Default prompt" value="Summarize the release notes" />
<StyledPromptInput ariaLabel="Prompt with placeholder" placeholder="Ask a question" />
<StyledPromptInput
ariaLabel="Prompt with action button"
value="Summarize the release notes"
actionButtonIconName="send"
/>
</SpaceBetween>
<SpaceBetween size="m" direction="horizontal">
<StyledPromptInput ariaLabel="Invalid prompt" value="Too short" invalid={true} actionButtonIconName="send" />
<StyledPromptInput
ariaLabel="Warning prompt"
value="Almost at the limit"
warning={true}
actionButtonIconName="send"
/>
<StyledPromptInput
ariaLabel="Read-only prompt"
value="Read-only text"
readOnly={true}
actionButtonIconName="send"
/>
</SpaceBetween>
<SpaceBetween size="m" direction="horizontal">
<StyledPromptInput
ariaLabel="Disabled prompt"
value="Cannot be edited"
disabled={true}
actionButtonIconName="send"
/>
<StyledPromptInput ariaLabel="Disabled prompt with placeholder" placeholder="Ask a question" disabled={true} />
</SpaceBetween>
<SpaceBetween size="m" direction="horizontal">
<StyledTokenPromptInput
ariaLabel="Prompt with references"
tokens={referenceTokens}
actionButtonIconName="send"
/>
<StyledTokenPromptInput
ariaLabel="Disabled prompt with references"
tokens={disabledReferenceTokens}
disabled={true}
/>
<StyledTokenPromptInput ariaLabel="Token mode prompt with placeholder" placeholder="Ask a question" />
<StyledTokenPromptInput
ariaLabel="Disabled token mode prompt with placeholder"
placeholder="Ask a question"
disabled={true}
/>
</SpaceBetween>
</SimplePage>
);
}
141 changes: 141 additions & 0 deletions pages/prompt-input/style-api-v2.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

@use '~components/styling/prompt-input';
@use '~components/styling/button';
@use '~components/styling/token';
@use '~components/styling/dropdown';
@use '../style-api/palette' as palette;

@mixin field {
@include prompt-input.root(
$background: palette.$surface,
$border-color: palette.$border,
$border-width: 2px,
$border-radius: 12px,
$box-shadow: 0 2px 4px palette.$subtle
);
}

.root {
@include field;

&:hover {
@include prompt-input.root($background: palette.$surface-alt, $border-color: palette.$border-accent);
}

&:focus-within {
@include prompt-input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
}
}

.root-invalid {
@include field;
@include prompt-input.root($background: palette.$error-surface, $border-color: palette.$error);

&:focus-within {
@include prompt-input.root($box-shadow: 0 0 0 3px palette.$error-hover);
}
}

.root-warning {
@include field;
@include prompt-input.root($background: palette.$warning-surface, $border-color: palette.$warning);

&:focus-within {
@include prompt-input.root($box-shadow: 0 0 0 3px palette.$warning);
}
}

.root-disabled {
@include field;
@include prompt-input.root(
$background: palette.$control-bg-disabled,
$border-color: palette.$border-disabled,
$box-shadow: none
);
}

.root-readonly {
@include field;
@include prompt-input.root($background: palette.$surface-alt);

&:focus-within {
@include prompt-input.root($border-color: palette.$accent, $box-shadow: 0 0 0 3px palette.$accent-active);
}
}

@mixin text {
@include prompt-input.textarea(
$color: palette.$text,
$padding-block: 10px,
$padding-inline: 16px,
$font-family: monospace,
$font-size: 15px,
$font-weight: 600,
$placeholder-color: palette.$accent,
$placeholder-font-size: 13px,
$placeholder-font-style: normal,
$placeholder-font-weight: 400
);
}

.textarea {
@include text;

&:hover {
@include prompt-input.textarea($color: palette.$accent-hover);
}

&:focus {
@include prompt-input.textarea($color: palette.$accent);
}
}

.textarea-disabled {
@include text;
@include prompt-input.textarea($color: palette.$text-disabled, $placeholder-color: palette.$text-disabled);
}

.textarea-readonly {
@include text;
@include prompt-input.textarea($color: palette.$text, $font-weight: 400);
}

.action-button {
@include button.root($color: palette.$accent, $focus-ring-color: palette.$accent);

&:hover {
@include button.root($color: palette.$accent-hover);
}
}

.token {
@include token.root(
$background: palette.$genai-surface,
$color: palette.$genai-text,
$border-color: palette.$genai,
$border-radius: 6px
);
}

.token-disabled {
@include token.root(
$background: palette.$control-bg-disabled,
$color: palette.$text-disabled,
$border-color: palette.$border-disabled,
$border-radius: 6px
);
}

.menu {
@include dropdown.container(
$background: palette.$surface-alt,
$border-radius: 12px,
$box-shadow: 0 4px 12px palette.$subtle-active,
$outline-color: palette.$accent,
$outline-width: 2px
);
}
Loading
Loading