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
104 changes: 53 additions & 51 deletions src/cards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { InternalContainerAsSubstep } from '../container/internal';
import { useInternalI18n } from '../i18n/context';
import { AnalyticsFunnelSubStep } from '../internal/analytics/components/analytics-funnel';
import { getBaseProps } from '../internal/base-component';
import Card from '../internal/card';
import { CollectionLabelContext } from '../internal/context/collection-label-context';
import { LinkDefaultVariantContext } from '../internal/context/link-default-variant-context';
import useBaseComponent from '../internal/hooks/use-base-component';
Expand Down Expand Up @@ -269,7 +270,6 @@ const CardsList = <T,>({
}) => {
const selectable = !!selectionType;
const canClickEntireCard = selectable && entireCardClickable;
const isRefresh = useVisualRefresh();

const { moveFocusDown, moveFocusUp } = useSelectionFocusMove(selectionType, items.length);

Expand Down Expand Up @@ -315,60 +315,62 @@ const CardsList = <T,>({
},
};
return (
<li
className={clsx(styles.card, {
[styles['card-selectable']]: selectable,
[styles['card-selected']]: selectable && selected,
})}
key={key}
<Card
action={
selectionProps && (
<div
className={styles['selection-control']}
{...(!canClickEntireCard && !disabled
? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata)
: {})}
>
<SelectionControl onFocusDown={moveFocusDown} onFocusUp={moveFocusUp} {...selectionProps} />
</div>
)
}
active={selectable && selected}
className={styles.card}
header={
<div className={clsx(styles['card-header'], analyticsSelectors['card-header'])}>
{cardDefinition.header ? cardDefinition.header(item) : ''}
</div>
}
innerMetadataAttributes={
entireCardClickable && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {}
}
key={index}
metadataAttributes={{
...getAnalyticsMetadataAttribute({
component: {
innerContext: {
position: `${index + 1}`,
item: `${key}`,
},
},
}),
...focusMarkers.item,
role: listItemRole,
}}
onClick={
canClickEntireCard
? event => {
selectionProps?.onChange();
// Manually move focus to the native input (checkbox or radio button)
event.currentTarget.querySelector('input')?.focus();
}
: undefined
}
onFocus={onFocus}
{...(focusMarkers && focusMarkers.item)}
role={listItemRole}
{...getAnalyticsMetadataAttribute({
component: {
innerContext: {
position: `${index + 1}`,
item: `${key}`,
},
},
})}
TagName="li"
>
<div
className={clsx(styles['card-inner'], isRefresh && styles.refresh)}
{...(canClickEntireCard && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {})}
onClick={
canClickEntireCard
? event => {
selectionProps?.onChange();
// Manually move focus to the native input (checkbox or radio button)
event.currentTarget.querySelector('input')?.focus();
}
: undefined
}
>
<div className={styles['card-header']}>
<div className={clsx(styles['card-header-inner'], analyticsSelectors['card-header'])}>
{cardDefinition.header ? cardDefinition.header(item) : ''}
</div>
{selectionProps && (
<div
className={styles['selection-control']}
{...(!canClickEntireCard && !disabled
? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata)
: {})}
>
<SelectionControl onFocusDown={moveFocusDown} onFocusUp={moveFocusUp} {...selectionProps} />
</div>
)}
{visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => (
<div key={id || index} className={styles.section} style={{ width: `${width}%` }}>
{header ? <div className={styles['section-header']}>{header}</div> : ''}
{content ? <div className={styles['section-content']}>{content(item)}</div> : ''}
</div>
{visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => (
<div key={id || index} className={styles.section} style={{ width: `${width}%` }}>
{header ? <div className={styles['section-header']}>{header}</div> : ''}
{content ? <div className={styles['section-content']}>{content(item)}</div> : ''}
</div>
))}
</div>
</li>
))}
</Card>
);
})}
</ol>
Expand Down
83 changes: 2 additions & 81 deletions src/cards/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,9 @@

@use '../internal/styles' as styles;
@use '../internal/styles/tokens' as awsui;
@use '../container/shared' as container;
@use './motion';

@mixin card-style {
border-start-start-radius: awsui.$border-radius-container;
border-start-end-radius: awsui.$border-radius-container;
border-end-start-radius: awsui.$border-radius-container;
border-end-end-radius: awsui.$border-radius-container;
box-sizing: border-box;

&::before {
@include styles.base-pseudo-element;
// Reset border color to prevent it from flashing black during card selection animation
border-color: transparent;
border-block-start: awsui.$border-container-top-width solid awsui.$color-border-container-top;
border-start-start-radius: awsui.$border-radius-container;
border-start-end-radius: awsui.$border-radius-container;
border-end-start-radius: awsui.$border-radius-container;
border-end-end-radius: awsui.$border-radius-container;
z-index: 1;
}

&::after {
@include styles.base-pseudo-element;
border-start-start-radius: awsui.$border-radius-container;
border-start-end-radius: awsui.$border-radius-container;
border-end-start-radius: awsui.$border-radius-container;
border-end-end-radius: awsui.$border-radius-container;
}
&:not(.refresh)::after {
box-shadow: awsui.$shadow-container;
}
&.refresh::after {
border-block: solid awsui.$border-divider-section-width awsui.$color-border-divider-default;
border-inline: solid awsui.$border-divider-section-width awsui.$color-border-divider-default;
}
}

.root {
@include styles.styles-reset();
@include styles.default-text-style;
}

.header {
Expand Down Expand Up @@ -111,49 +73,8 @@
}
}

.card {
display: flex;
overflow-wrap: break-word;
word-wrap: break-word;
margin-block: 0;
margin-inline: 0;
padding-block: 0;
padding-inline: 0;
list-style: none;
&-inner {
position: relative;
background-color: awsui.$color-background-container-content;
margin-block-start: 0;
margin-block-end: awsui.$space-grid-gutter;
margin-inline-start: awsui.$space-grid-gutter;
margin-inline-end: 0;
padding-block: awsui.$space-card-vertical;
padding-inline: awsui.$space-card-horizontal;
inline-size: 100%;
min-inline-size: 0;
@include card-style;
}
&-header {
@include styles.font-heading-m;
&-inner {
inline-size: 100%;
display: inline-block;
}
}
&-selectable {
> .card-inner > .card-header {
inline-size: 90%;
}
}
&-selected {
> .card-inner {
background-color: awsui.$color-background-item-selected;
&::before {
border-block: awsui.$border-item-width solid awsui.$color-border-item-selected;
border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected;
}
}
}
.card-header {
@include styles.font-heading-m;
}

.section {
Expand Down
49 changes: 49 additions & 0 deletions src/internal/card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import clsx from 'clsx';

import { useVisualRefresh } from '../hooks/use-visual-mode';
import { InternalCardProps } from './interfaces';

import styles from './styles.css.js';

export default function Card({
action,
active,
children,
className,
header,
innerMetadataAttributes,
metadataAttributes,
onClick,
onFocus,
role,
TagName = 'div',
}: InternalCardProps) {
const isRefresh = useVisualRefresh();

return (
<TagName
className={clsx(className, styles.card, styles.root, {
[styles['card-with-action']]: !!action,
[styles['card-active']]: active,
})}
onFocus={onFocus}
role={role}
{...metadataAttributes}
>
<div
className={clsx(styles['card-inner'], isRefresh && styles.refresh)}
{...innerMetadataAttributes}
onClick={onClick}
>
<div className={styles['card-header']}>
{header}
{action && <div className={styles.action}>{action}</div>}
</div>
{children}
</div>
</TagName>
);
}
56 changes: 56 additions & 0 deletions src/internal/card/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { FocusEventHandler } from 'react';

import { BaseComponentProps } from '../base-component';

export interface InternalCardProps extends BaseComponentProps {
/**
* Specifies an action for the card.
* It is recommended to use a button with inline-icon variant.
*/
action?: React.ReactNode;

/**
* Specifies whether the card is in active state.
*/
active?: boolean;

/**
* Optional URL for an image which will be displayed cropped as a background of the card.
* When this property is used, a dark gradient is overlayed and the text above defaults to bright colors.
* Make sure that any content you place on the card has sufficient contrast with the overlayed image behind.
*/
imageUrl?: string;

/**
* Primary content displayed in the card.
*/
children?: React.ReactNode;

/**
* Heading text.
*/
header?: React.ReactNode;

/**
* Icon which will be displayed at the top of the card,
* inline at the start of the content.
*/
icon?: React.ReactNode;

/**
* Called when the user clicks on the card.
*/
onClick?: React.MouseEventHandler<HTMLElement>;

onFocus?: FocusEventHandler<HTMLElement>;

role?: string;

TagName?: 'li' | 'div';

metadataAttributes: Record<string, string | undefined>;

innerMetadataAttributes: Record<string, string | undefined>;
}
4 changes: 2 additions & 2 deletions src/cards/motion.scss → src/internal/card/motion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
SPDX-License-Identifier: Apache-2.0
*/

@use '../internal/styles' as styles;
@use '../internal/styles/tokens' as awsui;
@use '../styles' as styles;
@use '../styles/tokens' as awsui;

.card-inner {
@include styles.with-motion {
Expand Down
Loading
Loading