WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
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
2 changes: 0 additions & 2 deletions pages/app/templates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ export function Page({
navigationHide={true}
activeDrawerId={toolsOpen ? "settings" : null}
onDrawerChange={({ detail }) => setToolsOpen(!!detail.activeDrawerId)}
tools={settings && <Drawer header={<Header variant="h2">Page settings</Header>}>{settings}</Drawer>}
toolsHide={!settings}
drawers={drawers}
content={
<Box>
Expand Down
111 changes: 111 additions & 0 deletions pages/chat-bubble/selectable-custom-card.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { useState } from "react";
import clsx from "clsx";

import Box from "@cloudscape-design/components/box";
import Button from "@cloudscape-design/components/button";
import Container from "@cloudscape-design/components/container";
import Header from "@cloudscape-design/components/header";
import Icon, { IconProps } from "@cloudscape-design/components/icon";
import SpaceBetween from "@cloudscape-design/components/space-between";

import { ChatBubble } from "../../lib/components";
import { Page } from "../app/templates";
import { ChatBubbleAvatarGenAI, ChatBubbleAvatarUser } from "./util-components";

import styles from "./styles.module.scss";

export default function ChatBubbleSelectableCustomPage() {
const [selectedCardId, setSelectedCardId] = useState<null | string>(null);
return (
<Page title="Chat bubble: selectable custom card">
<SpaceBetween direction="horizontal" size="l">
<Container header={<Header variant="h3">Chat container</Header>}>
<SpaceBetween size="m">
<ChatBubble avatar={<ChatBubbleAvatarUser />} type="outgoing" ariaLabel="User at 4:24:12pm">
<Box color="text-body-secondary">User request example</Box>
</ChatBubble>

<ChatBubble avatar={<ChatBubbleAvatarGenAI />} type="incoming" ariaLabel="Gen AI at 4:24:24pm">
<Box color="text-body-secondary">Normal response example</Box>
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI />}
hideAvatar={true}
type="incoming"
ariaLabel="Gen AI at 4:24:25pm"
additionalContent={
<SpaceBetween size="s" direction="horizontal">
<CustomSelectableCard
header="Selectable card 1"
pressed={selectedCardId === "1"}
iconName="expand"
iconNamePressed="shrink"
onClick={() => setSelectedCardId(selectedCardId === "1" ? null : "1")}
>
Selectable card 1 content
</CustomSelectableCard>
<CustomSelectableCard
header="Selectable card 2"
pressed={selectedCardId === "2"}
iconName="expand"
iconNamePressed="shrink"
onClick={() => setSelectedCardId(selectedCardId === "2" ? null : "2")}
>
Selectable card 2 content
</CustomSelectableCard>
</SpaceBetween>
}
>
Selectable response example
</ChatBubble>
</SpaceBetween>
</Container>

<Container header={<Header variant="h3">Presentation container</Header>}>
{selectedCardId ? (
<SpaceBetween size="m">
<Box>{selectedCardId === "1" ? "First" : "Second"} card is selected</Box>
<Button onClick={() => setSelectedCardId(null)}>Clear selection</Button>
</SpaceBetween>
) : null}
</Container>
</SpaceBetween>
</Page>
);
}

function CustomSelectableCard({
header,
children,
iconName,
iconNamePressed,
pressed,
onClick,
}: {
header: React.ReactNode;
children: React.ReactNode;
iconName: IconProps.Name;
iconNamePressed: IconProps.Name;
pressed: boolean;
onClick: () => void;
}) {
return (
<button
aria-pressed={pressed}
className={clsx(styles["selectable-card"], pressed && styles["selectable-card-pressed"])}
onClick={onClick}
>
<SpaceBetween size="s">
<div className={styles["selectable-card-header"]}>
<Box fontWeight="bold">{header}</Box>
<Icon name={pressed ? iconNamePressed : iconName} />
</div>
<Box>{children}</Box>
</SpaceBetween>
</button>
);
}
82 changes: 82 additions & 0 deletions pages/chat-bubble/selectable.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { useState } from "react";

import Box from "@cloudscape-design/components/box";
import Button from "@cloudscape-design/components/button";
import Container from "@cloudscape-design/components/container";
import Header from "@cloudscape-design/components/header";
import SpaceBetween from "@cloudscape-design/components/space-between";

import { ChatBubble } from "../../lib/components";
import { Page } from "../app/templates";
import { ChatBubbleAvatarGenAI, ChatBubbleAvatarUser } from "./util-components";

export default function ChatBubbleSelectablePage() {
const [selectedCardId, setSelectedCardId] = useState<null | string>(null);
return (
<Page title="Chat bubble: selectable">
<SpaceBetween direction="horizontal" size="l">
<Container header={<Header variant="h3">Chat container</Header>}>
<SpaceBetween size="m">
<ChatBubble avatar={<ChatBubbleAvatarUser />} type="outgoing" ariaLabel="User at 4:24:12pm">
<Box color="text-body-secondary">User request example</Box>
</ChatBubble>

<ChatBubble avatar={<ChatBubbleAvatarGenAI />} type="incoming" ariaLabel="Gen AI at 4:24:24pm">
<Box color="text-body-secondary">Normal response example</Box>
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI />}
hideAvatar={true}
type="incoming"
ariaLabel="Gen AI at 4:24:25pm"
selectionType="click"
onSelect={() => setSelectedCardId((prev) => (prev !== "1" ? "1" : null))}
selected={selectedCardId === "1"}
>
<Box variant="h4">Selectable response example</Box>
<Box variant="p">This entire card is clickable</Box>
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI />}
hideAvatar={true}
type="incoming"
ariaLabel="Gen AI at 4:24:25pm"
selectionType="custom"
selected={selectedCardId === "2"}
>
<div style={{ display: "flex", gap: 8, justifyContent: "space-between" }}>
<Box variant="h4">Selectable response example</Box>
{selectedCardId !== "2" ? (
<Button variant="inline-link" onClick={() => setSelectedCardId("2")}>
Show
</Button>
) : (
<Button variant="inline-link" onClick={() => setSelectedCardId(null)}>
Hide
</Button>
)}
</div>
<Box variant="p">
This entire card is not clickable. Instead, there is a custom action in the top-right.
</Box>
</ChatBubble>
</SpaceBetween>
</Container>

<Container header={<Header variant="h3">Presentation container</Header>}>
{selectedCardId ? (
<SpaceBetween size="m">
<Box>{selectedCardId === "1" ? "First" : "Second"} card is selected</Box>
<Button onClick={() => setSelectedCardId(null)}>Clear selection</Button>
</SpaceBetween>
) : null}
</Container>
</SpaceBetween>
</Page>
);
}
91 changes: 91 additions & 0 deletions pages/chat-bubble/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

@use "../../node_modules/@cloudscape-design/design-tokens/index.scss" as cs;

@mixin styles-reset {
border-collapse: separate;
border-spacing: 0;
box-sizing: border-box;
caption-side: top;
cursor: auto;
direction: inherit;
empty-cells: show;
font-family: serif;
font-size: medium;
font-style: normal;
font-variant: normal;
font-weight: 400;
font-stretch: normal;
line-height: normal;
hyphens: none;
letter-spacing: normal;
list-style: disc outside none;
tab-size: 8;
text-align: start;
text-indent: 0;
text-shadow: none;
text-transform: none;
visibility: visible;
white-space: normal;
word-spacing: normal;
}

@mixin focus-highlight(
$gutter: 4px,
$border-radius: cs.$border-radius-control-default-focus-ring,
$border-color: cs.$color-border-item-focused,
$border-width: 2px
) {
position: relative;
box-sizing: border-box;
outline: 2px dotted transparent;
outline-offset: calc($gutter - 1px);

&::before {
content: " ";
display: block;
position: absolute;
box-sizing: border-box;
inset-inline-start: calc(-1 * #{$gutter});
inset-block-start: calc(-1 * #{$gutter});
inline-size: calc(100% + 2 * #{$gutter});
block-size: calc(100% + 2 * #{$gutter});
border-radius: $border-radius;
border: $border-width solid $border-color;
}
}

.selectable-card {
@include styles-reset;
background: cs.$color-background-layout-main;
border-radius: 8px;
border: 2px solid cs.$color-border-divider-default;
padding: cs.$space-scaled-s cs.$space-scaled-s;
cursor: pointer;

&:hover {
background: cs.$color-background-dropdown-item-hover;
}

&-pressed {
border-color: cs.$color-border-item-selected;
background: cs.$color-background-item-selected;

&:hover {
background: cs.$color-background-item-selected;
}
}

&:focus-visible {
@include focus-highlight(8px, 8px);
}
}

.selectable-card-header {
display: flex;
justify-content: space-between;
gap: cs.$space-static-s;
}
Loading
Loading