diff --git a/.changeset/moody-socks-win.md b/.changeset/moody-socks-win.md new file mode 100644 index 0000000000..883299fa51 --- /dev/null +++ b/.changeset/moody-socks-win.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new TableCellProfile component for Table and DataTable in Canon. diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index bb1ba10018..0a3f101243 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -246,6 +246,48 @@ display: flex; } +.canon-TableCellProfile { + gap: var(--canon-space-2); + flex-direction: row; + align-items: center; + display: flex; +} + +.canon-TableCellProfileAvatar { + vertical-align: middle; + user-select: none; + color: var(--canon-fg-primary); + background-color: var(--canon-bg-surface-2); + border-radius: 100%; + justify-content: center; + align-items: center; + width: 1.25rem; + height: 1.25rem; + font-size: 1rem; + font-weight: 500; + line-height: 1; + display: inline-flex; + overflow: hidden; +} + +.canon-TableCellProfileAvatarImage { + object-fit: cover; + width: 100%; + height: 100%; +} + +.canon-TableCellProfileAvatarFallback { + width: 100%; + height: 100%; + font-size: var(--canon-font-size-2); + font-weight: var(--canon-font-weight-regular); + box-shadow: inset 0 0 0 1px var(--canon-border); + border-radius: var(--canon-radius-full); + justify-content: center; + align-items: center; + display: flex; +} + .canon-Text { font-family: var(--canon-font-regular); margin: 0; diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 473b8f5d45..d08db21d43 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9470,6 +9470,48 @@ display: flex; } +.canon-TableCellProfile { + gap: var(--canon-space-2); + flex-direction: row; + align-items: center; + display: flex; +} + +.canon-TableCellProfileAvatar { + vertical-align: middle; + user-select: none; + color: var(--canon-fg-primary); + background-color: var(--canon-bg-surface-2); + border-radius: 100%; + justify-content: center; + align-items: center; + width: 1.25rem; + height: 1.25rem; + font-size: 1rem; + font-weight: 500; + line-height: 1; + display: inline-flex; + overflow: hidden; +} + +.canon-TableCellProfileAvatarImage { + object-fit: cover; + width: 100%; + height: 100%; +} + +.canon-TableCellProfileAvatarFallback { + width: 100%; + height: 100%; + font-size: var(--canon-font-size-2); + font-weight: var(--canon-font-weight-regular); + box-shadow: inset 0 0 0 1px var(--canon-border); + border-radius: var(--canon-radius-full); + justify-content: center; + align-items: center; + display: flex; +} + .canon-Text { font-family: var(--canon-font-regular); margin: 0; diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 1e65a51dca..b0b9d3d0a9 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -260,6 +260,9 @@ export const DataTable: { TableCellLink: ForwardRefExoticComponent< TableCellLinkProps & RefAttributes >; + TableCellProfile: ForwardRefExoticComponent< + TableCellProfileProps & RefAttributes + >; TableHead: ForwardRefExoticComponent< ThHTMLAttributes & RefAttributes >; @@ -1116,6 +1119,9 @@ export const Table: { CellLink: ForwardRefExoticComponent< TableCellLinkProps & RefAttributes >; + CellProfile: ForwardRefExoticComponent< + TableCellProfileProps & RefAttributes + >; Caption: ForwardRefExoticComponent< HTMLAttributes & RefAttributes @@ -1124,7 +1130,7 @@ export const Table: { // @public (undocumented) export interface TableCellLinkProps - extends React.TdHTMLAttributes { + extends React.HTMLAttributes { // (undocumented) description?: string; // (undocumented) @@ -1135,9 +1141,22 @@ export interface TableCellLinkProps title: string; } +// @public (undocumented) +export interface TableCellProfileProps + extends React.HTMLAttributes { + // (undocumented) + name?: string; + // (undocumented) + src?: string; + // (undocumented) + to?: string; + // (undocumented) + withImage?: boolean; +} + // @public (undocumented) export interface TableCellTextProps - extends React.TdHTMLAttributes { + extends React.HTMLAttributes { // (undocumented) description?: string; // (undocumented) diff --git a/packages/canon/src/components/DataTable/DataTable.tsx b/packages/canon/src/components/DataTable/DataTable.tsx index 8be9ae174e..5a587df56f 100644 --- a/packages/canon/src/components/DataTable/DataTable.tsx +++ b/packages/canon/src/components/DataTable/DataTable.tsx @@ -46,5 +46,6 @@ export const DataTable = { TableCell: Table.Cell, TableCellText: Table.CellText, TableCellLink: Table.CellLink, + TableCellProfile: Table.CellProfile, TableHead: Table.Head, }; diff --git a/packages/canon/src/components/DataTable/mocked-columns.tsx b/packages/canon/src/components/DataTable/mocked-columns.tsx index 9f2ce72378..d2a6cfa24e 100644 --- a/packages/canon/src/components/DataTable/mocked-columns.tsx +++ b/packages/canon/src/components/DataTable/mocked-columns.tsx @@ -58,7 +58,7 @@ export const columns: ColumnDef[] = [ accessorKey: 'owner', header: 'Owner', cell: ({ row }) => ( - + ), }, { diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index 185c110654..abc0ce03c8 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -19,6 +19,7 @@ import clsx from 'clsx'; import { TableCell } from './TableCell/TableCell'; import { TableCellText } from './TableCellText/TableCellText'; import { TableCellLink } from './TableCellLink/TableCellLink'; +import { TableCellProfile } from './TableCellProfile/TableCellProfile'; const TableRoot = forwardRef< HTMLTableElement, @@ -91,5 +92,6 @@ export const Table = { Cell: TableCell, CellText: TableCellText, CellLink: TableCellLink, + CellProfile: TableCellProfile, Caption: TableCaption, }; diff --git a/packages/canon/src/components/Table/TableCellLink/types.ts b/packages/canon/src/components/Table/TableCellLink/types.ts index 50cac9a45a..51df3724cf 100644 --- a/packages/canon/src/components/Table/TableCellLink/types.ts +++ b/packages/canon/src/components/Table/TableCellLink/types.ts @@ -18,7 +18,7 @@ import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ export interface TableCellLinkProps - extends React.TdHTMLAttributes { + extends React.HTMLAttributes { title: string; description?: string; href: string; diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx new file mode 100644 index 0000000000..5ee482f4fc --- /dev/null +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { TableCellProfile } from './TableCellProfile'; + +const meta = { + title: 'Components/Table/TableCellProfile', + component: TableCellProfile, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + src: 'https://avatars.githubusercontent.com/u/1540635?v=4', + name: 'Charles de Dreuille', + }, +}; + +export const Fallback: Story = { + args: { + ...Default.args, + src: 'https://avatars.githubusercontent.com/u/15406AAAAAAAAA', + }, +}; + +export const WithLink: Story = { + args: { + ...Default.args, + to: 'https://www.google.com', + }, +}; diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css new file mode 100644 index 0000000000..30ab1fe683 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css @@ -0,0 +1,57 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.canon-TableCellProfile { + display: flex; + flex-direction: row; + gap: var(--canon-space-2); + align-items: center; +} + +.canon-TableCellProfileAvatar { + display: inline-flex; + justify-content: center; + align-items: center; + vertical-align: middle; + border-radius: 100%; + user-select: none; + font-weight: 500; + color: var(--canon-fg-primary); + background-color: var(--canon-bg-surface-2); + font-size: 1rem; + line-height: 1; + overflow: hidden; + height: 1.25rem; + width: 1.25rem; +} + +.canon-TableCellProfileAvatarImage { + object-fit: cover; + height: 100%; + width: 100%; +} + +.canon-TableCellProfileAvatarFallback { + align-items: center; + display: flex; + justify-content: center; + height: 100%; + width: 100%; + font-size: var(--canon-font-size-2); + font-weight: var(--canon-font-weight-regular); + box-shadow: inset 0 0 0 1px var(--canon-border); + border-radius: var(--canon-radius-full); +} diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx new file mode 100644 index 0000000000..78d82f59aa --- /dev/null +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx @@ -0,0 +1,60 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import clsx from 'clsx'; +import { TableCellProfileProps } from './types'; +import { Text } from '../../Text/Text'; +import { Link } from '../../Link/Link'; +import { Avatar } from '@base-ui-components/react/avatar'; + +/** @public */ +const TableCellProfile = forwardRef( + ({ className, src, name, to, withImage = true, ...rest }, ref) => ( +
+ {withImage && ( + + + + {(name || '') + .split(' ') + .map(word => word[0]) + .join('') + .toLocaleUpperCase('en-US') + .slice(0, 1)} + + + )} + {name && to ? ( + {name} + ) : ( + {name} + )} +
+ ), +); +TableCellProfile.displayName = 'TableCellProfile'; + +export { TableCellProfile }; diff --git a/packages/canon/src/components/Table/TableCellProfile/types.ts b/packages/canon/src/components/Table/TableCellProfile/types.ts new file mode 100644 index 0000000000..d5a7b18cc2 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellProfile/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @public */ +export interface TableCellProfileProps + extends React.HTMLAttributes { + src?: string; + name?: string; + to?: string; + withImage?: boolean; +} diff --git a/packages/canon/src/components/Table/TableCellText/types.ts b/packages/canon/src/components/Table/TableCellText/types.ts index c6f1814966..99a27ad248 100644 --- a/packages/canon/src/components/Table/TableCellText/types.ts +++ b/packages/canon/src/components/Table/TableCellText/types.ts @@ -16,7 +16,7 @@ /** @public */ export interface TableCellTextProps - extends React.TdHTMLAttributes { + extends React.HTMLAttributes { title: string; description?: string; } diff --git a/packages/canon/src/components/Table/index.ts b/packages/canon/src/components/Table/index.ts index 0422086b45..5c7f3da195 100644 --- a/packages/canon/src/components/Table/index.ts +++ b/packages/canon/src/components/Table/index.ts @@ -17,3 +17,4 @@ export * from './Table'; export * from './TableCellText/types'; export * from './TableCellLink/types'; +export * from './TableCellProfile/types'; diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 7dee4afb97..aaa22acb7a 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -27,6 +27,7 @@ @import '../components/Table/TableCell/TableCell.styles.css'; @import '../components/Table/TableCellText/TableCellText.styles.css'; @import '../components/Table/TableCellLink/TableCellLink.styles.css'; +@import '../components/Table/TableCellProfile/TableCellProfile.styles.css'; @import '../components/Text/styles.css'; @import '../components/Heading/styles.css'; @import '../components/IconButton/styles.css';