diff --git a/packages/carbon-components-react/scss/components/skeleton-styles/_ai-skeleton-styles.scss b/packages/carbon-components-react/scss/components/skeleton-styles/_ai-skeleton-styles.scss new file mode 100644 index 000000000000..c07f6ee77f00 --- /dev/null +++ b/packages/carbon-components-react/scss/components/skeleton-styles/_ai-skeleton-styles.scss @@ -0,0 +1,9 @@ +// Code generated by carbon-components-react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2023 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/components/skeleton-styles/ai-skeleton-styles'; diff --git a/packages/carbon-components/scss/components/skeleton-styles/_ai-skeleton-styles.scss b/packages/carbon-components/scss/components/skeleton-styles/_ai-skeleton-styles.scss new file mode 100644 index 000000000000..b75ddc45dd0a --- /dev/null +++ b/packages/carbon-components/scss/components/skeleton-styles/_ai-skeleton-styles.scss @@ -0,0 +1,9 @@ +// Code generated by carbon-components. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2023 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/components/skeleton-styles/ai-skeleton-styles'; diff --git a/packages/elements/src/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/elements/src/__tests__/__snapshots__/PublicAPI-test.js.snap index 013b80199dae..ed0294894503 100644 --- a/packages/elements/src/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/elements/src/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -17,6 +17,8 @@ Array [ "aiGradientStart02", "aiInnerShadow", "aiOverlay", + "aiSkeletonBackground", + "aiSkeletonElementBackground", "background", "backgroundActive", "backgroundBrand", diff --git a/packages/react/scss/components/skeleton-styles/_ai-skeleton-styles.scss b/packages/react/scss/components/skeleton-styles/_ai-skeleton-styles.scss new file mode 100644 index 000000000000..c29b3da911ef --- /dev/null +++ b/packages/react/scss/components/skeleton-styles/_ai-skeleton-styles.scss @@ -0,0 +1,9 @@ +// Code generated by @carbon/react. DO NOT EDIT. +// +// Copyright IBM Corp. 2018, 2023 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward '@carbon/styles/scss/components/skeleton-styles/ai-skeleton-styles'; diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonIcon.stories.js b/packages/react/src/components/AiSkeleton/AiSkeletonIcon.stories.js new file mode 100644 index 000000000000..0c435924623e --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonIcon.stories.js @@ -0,0 +1,38 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* eslint-disable no-console */ + +import React from 'react'; + +import AiSkeletonIcon from './AiSkeletonIcon'; + +export default { + title: 'Experimental/unstable__AiSkeleton/AiSkeletonIcon', + component: AiSkeletonIcon, +}; + +const propsSkeleton = { + style: { + margin: '50px', + }, +}; + +const propsSkeleton2 = { + style: { + margin: '50px', + width: '24px', + height: '24px', + }, +}; + +export const Default = () => ( + <> + + + +); diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonIcon.tsx b/packages/react/src/components/AiSkeleton/AiSkeletonIcon.tsx new file mode 100644 index 000000000000..e85843607273 --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonIcon.tsx @@ -0,0 +1,47 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes from 'prop-types'; +import React from 'react'; +import classNames from 'classnames'; +import { usePrefix } from '../../internal/usePrefix'; +import { SkeletonIcon } from '../SkeletonIcon'; + +interface AiSkeletonIconProps { + /** + * Specify an optional className to add. + */ + className?: string; + + /** + * The CSS styles. + */ + style?: React.CSSProperties; +} + +const AiSkeletonIcon = ({ className, ...rest }: AiSkeletonIconProps) => { + const prefix = usePrefix(); + const AiSkeletonIconClasses = classNames(className, { + [`${prefix}--skeleton__icon--ai`]: true, + }); + + return ; +}; + +AiSkeletonIcon.propTypes = { + /** + * Specify an optional className to add. + */ + className: PropTypes.string, + + /** + * The CSS styles. + */ + style: PropTypes.object, +}; + +export default AiSkeletonIcon; diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.stories.js b/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.stories.js new file mode 100644 index 000000000000..5a6b323c50bf --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.stories.js @@ -0,0 +1,19 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* eslint-disable no-console */ + +import React from 'react'; + +import AiSkeletonPlaceholder from './AiSkeletonPlaceholder'; + +export default { + title: 'Experimental/unstable__AiSkeleton/AiSkeletonPlaceholder', + component: AiSkeletonPlaceholder, +}; + +export const Default = () => ; diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.tsx b/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.tsx new file mode 100644 index 000000000000..cc5e849a4fb9 --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonPlaceholder.tsx @@ -0,0 +1,44 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { usePrefix } from '../../internal/usePrefix'; +import { SkeletonPlaceholder } from '../SkeletonPlaceholder'; + +export interface AiSkeletonPlaceholderProps { + /** + * Add a custom class to the component to set the height and width + */ + className?: string; +} + +const AiSkeletonPlaceholder = ({ + className, + ...other +}: AiSkeletonPlaceholderProps) => { + const prefix = usePrefix(); + const AiSkeletonPlaceholderClasses = classNames( + { className, [`${prefix}--skeleton__placeholder--ai`]: true }, + className + ); + + return ( + + ); +}; + +AiSkeletonPlaceholder.propTypes = { + /** + * Add a custom class to the component + * to set the height and width + */ + className: PropTypes.string, +}; + +export default AiSkeletonPlaceholder; diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonText.stories.js b/packages/react/src/components/AiSkeleton/AiSkeletonText.stories.js new file mode 100644 index 000000000000..5f75ea23a228 --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonText.stories.js @@ -0,0 +1,54 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* eslint-disable no-console */ + +import React from 'react'; + +import AiSkeletonText from './AiSkeletonText'; + +export default { + title: 'Experimental/unstable__AiSkeleton/AiSkeletonText', + component: AiSkeletonText, +}; + +export const Default = () => ; + +export const Playground = (args) => ; + +Playground.args = { + heading: false, + paragraph: false, + width: '100%', + lineCount: 3, +}; + +Playground.argTypes = { + className: { + control: false, + }, + heading: { + control: { + type: 'boolean', + }, + }, + paragraph: { + control: { + type: 'boolean', + }, + }, + width: { + control: { + type: 'text', + }, + }, + lineCount: { + control: { + type: 'number', + }, + }, +}; diff --git a/packages/react/src/components/AiSkeleton/AiSkeletonText.tsx b/packages/react/src/components/AiSkeleton/AiSkeletonText.tsx new file mode 100644 index 000000000000..69a1b3a085e1 --- /dev/null +++ b/packages/react/src/components/AiSkeleton/AiSkeletonText.tsx @@ -0,0 +1,73 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes from 'prop-types'; +import React from 'react'; +import classNames from 'classnames'; +import { usePrefix } from '../../internal/usePrefix'; +import { SkeletonText } from '../SkeletonText'; + +interface AiSkeletonTextProps { + /** + * Specify an optional className to be applied to the container node. + */ + className?: string; + + /** + * Generates skeleton text at a larger size. + */ + heading?: boolean; + + /** + * The number of lines shown if paragraph is true. + */ + lineCount?: number; + + /** + * Set this to true to generate multiple lines of text. + */ + paragraph?: boolean; + + /** + * Width (in px or %) of single line of text or max-width of paragraph lines. + */ + width?: string; +} + +const AiSkeletonText = ({ className, ...rest }: AiSkeletonTextProps) => { + const prefix = usePrefix(); + const aiSkeletonTextClasses = classNames(className, { + [`${prefix}--skeleton__text--ai`]: true, + }); + + return ; +}; + +AiSkeletonText.propTypes = { + /** + * Specify an optional className to be applied to the container node + */ + className: PropTypes.string, + /** + * generates skeleton text at a larger size + */ + heading: PropTypes.bool, + /** + * the number of lines shown if paragraph is true + */ + lineCount: PropTypes.number, + /** + * will generate multiple lines of text + */ + paragraph: PropTypes.bool, + /** + * width (in px or %) of single line of text or max-width of paragraph lines + */ + width: PropTypes.string, +}; + +export default AiSkeletonText; diff --git a/packages/react/src/components/AiSkeleton/index.tsx b/packages/react/src/components/AiSkeleton/index.tsx new file mode 100644 index 000000000000..439f4ce25736 --- /dev/null +++ b/packages/react/src/components/AiSkeleton/index.tsx @@ -0,0 +1,12 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import AiSkeletonPlaceholder from './AiSkeletonPlaceholder'; +import AiSkeletonIcon from './AiSkeletonIcon'; +import AiSkeletonText from './AiSkeletonText'; + +export { AiSkeletonText, AiSkeletonIcon, AiSkeletonPlaceholder }; diff --git a/packages/react/src/index.js b/packages/react/src/index.js index b60de814261e..d3fd15860de2 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -305,6 +305,12 @@ export { SlugContent as unstable__SlugContent, SlugActions as unstable__SlugActions, } from './components/Slug'; + +export { + AiSkeletonText as unstable__AiSkeletonText, + AiSkeletonIcon as unstable__AiSkeletonIcon, + AiSkeletonPlaceholder as unstable__AiSkeletonPlaceholder, +} from './components/AiSkeleton'; export { ChatButton as unstable__ChatButton, ChatButtonSkeleton as unstable__ChatButtonSkeleton, diff --git a/packages/styles/__tests__/__snapshots__/styles-test.js.snap b/packages/styles/__tests__/__snapshots__/styles-test.js.snap index 6dd6b5054b11..8e05a7caa4f1 100644 --- a/packages/styles/__tests__/__snapshots__/styles-test.js.snap +++ b/packages/styles/__tests__/__snapshots__/styles-test.js.snap @@ -617,6 +617,11 @@ Array [ "importPath": "@carbon/styles/scss/components/skeleton-styles/skeleton-styles", "relativePath": "scss/components/skeleton-styles/skeleton-styles", }, + Object { + "filepath": "scss/components/skeleton-styles/_ai-skeleton-styles.scss", + "importPath": "@carbon/styles/scss/components/skeleton-styles/ai-skeleton-styles", + "relativePath": "scss/components/skeleton-styles/ai-skeleton-styles", + }, Object { "filepath": "scss/components/slider/_index.scss", "importPath": "@carbon/styles/scss/components/slider", diff --git a/packages/styles/files.js b/packages/styles/files.js index 6bb799fec74c..75bd138e0133 100644 --- a/packages/styles/files.js +++ b/packages/styles/files.js @@ -142,6 +142,7 @@ const files = [ 'scss/components/select/_select.scss', 'scss/components/skeleton-styles/_index.scss', 'scss/components/skeleton-styles/_skeleton-styles.scss', + 'scss/components/skeleton-styles/_ai-skeleton-styles.scss', 'scss/components/slider/_index.scss', 'scss/components/slider/_slider.scss', 'scss/components/slug/_index.scss', diff --git a/packages/styles/scss/__tests__/theme-test.js b/packages/styles/scss/__tests__/theme-test.js index 0e166aed8762..933bdc1a2e10 100644 --- a/packages/styles/scss/__tests__/theme-test.js +++ b/packages/styles/scss/__tests__/theme-test.js @@ -162,6 +162,8 @@ describe('@carbon/styles/scss/theme', () => { "ai-border-start", "ai-border-end", "ai-drop-shadow", + "ai-skeleton-background", + "ai-skeleton-element-background", "ai-overlay", "slug-callout-caret-center", "slug-callout-caret-bottom", diff --git a/packages/styles/scss/components/skeleton-styles/_ai-skeleton-styles.scss b/packages/styles/scss/components/skeleton-styles/_ai-skeleton-styles.scss new file mode 100644 index 000000000000..9d2b24431a70 --- /dev/null +++ b/packages/styles/scss/components/skeleton-styles/_ai-skeleton-styles.scss @@ -0,0 +1,66 @@ +// +// Copyright IBM Corp. 2016, 2024 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@use '../../config' as *; +@use '../../theme'; +@use '../../utilities/convert'; + +@keyframes ai-skeleton-animation { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } +} + +/// AI Skeleton styles +/// @access public +/// @group skeleton +@mixin ai-skeleton-styles { + .#{$prefix}--skeleton__text--ai, + .#{$prefix}--skeleton__placeholder--ai, + .#{$prefix}--skeleton__icon--ai { + overflow: hidden; + background: theme.$ai-skeleton-background; + } + + .#{$prefix}--skeleton__text--ai::before, + .#{$prefix}--skeleton__placeholder--ai::before, + .#{$prefix}--skeleton__icon--ai::before { + animation: 1250ms ease-in-out ai-skeleton-animation infinite; + background: linear-gradient( + to right, + rgba(theme.get('ai-skeleton-element-background'), 0) 0%, + rgba(theme.get('ai-skeleton-element-background'), 0.5) 50%, + rgba(theme.get('ai-skeleton-element-background'), 0) 100% + ); + } + + .#{$prefix}--skeleton__placeholder--ai::before, + .#{$prefix}--skeleton__icon--ai::before { + inline-size: 200%; + } + + // //skeleton placeholder + .#{$prefix}--skeleton__placeholder--ai { + border-radius: convert.to-rem(8px); + } + + //skeleton text + .#{$prefix}--skeleton__text--ai { + border-radius: convert.to-rem(16px); + } + + //skeleton icon + .#{$prefix}--skeleton__icon--ai { + border-radius: convert.to-rem(2px); + } +} + +// rgba($ai-skeleton-element-background, 1) diff --git a/packages/styles/scss/components/skeleton-styles/_index.scss b/packages/styles/scss/components/skeleton-styles/_index.scss index 6db579792184..f42f370d822e 100644 --- a/packages/styles/scss/components/skeleton-styles/_index.scss +++ b/packages/styles/scss/components/skeleton-styles/_index.scss @@ -6,6 +6,9 @@ // @forward 'skeleton-styles'; +@forward 'ai-skeleton-styles'; @use 'skeleton-styles'; +@use 'ai-skeleton-styles'; @include skeleton-styles.skeleton-styles; +@include ai-skeleton-styles.ai-skeleton-styles; diff --git a/packages/themes/src/g10.js b/packages/themes/src/g10.js index 57010fe81d56..b4dba55dd464 100644 --- a/packages/themes/src/g10.js +++ b/packages/themes/src/g10.js @@ -9,6 +9,7 @@ import { // Blue blue10, blue20, + blue20Hover, blue30, blue40, blue60, @@ -233,6 +234,10 @@ export const aiAuraHoverEnd = rgba(white, 0); export const slugCalloutShadowOuter01 = rgba(blue70, 0.25); export const slugCalloutShadowOuter02 = rgba(black, 0.1); +// AI skeleton +export const aiSkeletonBackground = blue20Hover; +export const aiSkeletonElementBackground = blue100; + // AI Modal tokens export const aiOverlay = rgba(blue100, 0.5); diff --git a/packages/themes/src/g100.js b/packages/themes/src/g100.js index a4cd52fdbdf7..f064bbe5dc23 100644 --- a/packages/themes/src/g100.js +++ b/packages/themes/src/g100.js @@ -12,6 +12,7 @@ import { blue40, blue60, blue70, + blue70Hover, blue80, blue100, @@ -238,6 +239,10 @@ export const aiAuraHoverEnd = rgba(black, 0); export const slugCalloutShadowOuter01 = rgba(blue80, 0.25); export const slugCalloutShadowOuter02 = rgba(black, 0.65); +// AI skeleton +export const aiSkeletonBackground = blue70Hover; +export const aiSkeletonElementBackground = blue100; + // AI Modal tokens export const aiOverlay = rgba(blue100, 0.5); diff --git a/packages/themes/src/g90.js b/packages/themes/src/g90.js index d394fac245a7..541277860acf 100644 --- a/packages/themes/src/g90.js +++ b/packages/themes/src/g90.js @@ -12,6 +12,7 @@ import { blue40, blue60, blue70, + blue70Hover, blue80, blue100, @@ -239,6 +240,10 @@ export const aiAuraHoverEnd = rgba(black, 0); export const slugCalloutShadowOuter01 = rgba(blue80, 0.25); export const slugCalloutShadowOuter02 = rgba(black, 0.65); +// AI skeleton +export const aiSkeletonBackground = blue70Hover; +export const aiSkeletonElementBackground = blue100; + // AI Modal tokens export const aiOverlay = rgba(blue100, 0.5); diff --git a/packages/themes/src/tokens/__tests__/__snapshots__/v11-test.js.snap b/packages/themes/src/tokens/__tests__/__snapshots__/v11-test.js.snap index 991d6d77023c..20d230f3bbef 100644 --- a/packages/themes/src/tokens/__tests__/__snapshots__/v11-test.js.snap +++ b/packages/themes/src/tokens/__tests__/__snapshots__/v11-test.js.snap @@ -273,6 +273,8 @@ Array [ "ai-border-start", "ai-border-end", "ai-drop-shadow", + "ai-skeleton-background", + "ai-skeleton-element-background", "ai-overlay", "slug-callout-caret-center", "slug-callout-caret-bottom", diff --git a/packages/themes/src/tokens/__tests__/metadata-test.js b/packages/themes/src/tokens/__tests__/metadata-test.js index e27f2ccaab98..af39472abb05 100644 --- a/packages/themes/src/tokens/__tests__/metadata-test.js +++ b/packages/themes/src/tokens/__tests__/metadata-test.js @@ -1065,6 +1065,14 @@ test('metadata', () => { "name": "ai-drop-shadow", "type": "color", }, + Object { + "name": "ai-skeleton-background", + "type": "color", + }, + Object { + "name": "ai-skeleton-element-background", + "type": "color", + }, Object { "name": "ai-overlay", "type": "color", diff --git a/packages/themes/src/tokens/v11TokenGroup.js b/packages/themes/src/tokens/v11TokenGroup.js index 08c1bfefcfe2..fe024e7e7b00 100644 --- a/packages/themes/src/tokens/v11TokenGroup.js +++ b/packages/themes/src/tokens/v11TokenGroup.js @@ -382,6 +382,8 @@ export const ai = TokenGroup.create({ 'ai-border-start', 'ai-border-end', 'ai-drop-shadow', + 'ai-skeleton-background', + 'ai-skeleton-element-background', 'ai-overlay', // Caret tokens 'slug-callout-caret-center', diff --git a/packages/themes/src/white.js b/packages/themes/src/white.js index bb2a67c55432..bfedc4095c7e 100644 --- a/packages/themes/src/white.js +++ b/packages/themes/src/white.js @@ -9,6 +9,7 @@ import { // Blue blue10, blue20, + blue20Hover, blue30, blue40, blue60, @@ -233,6 +234,10 @@ export const aiAuraHoverEnd = rgba(white, 0); export const slugCalloutShadowOuter01 = rgba(blue70, 0.25); export const slugCalloutShadowOuter02 = rgba(black, 0.1); +// AI skeleton +export const aiSkeletonBackground = blue20Hover; +export const aiSkeletonElementBackground = blue100; + // AI Modal tokens export const aiOverlay = rgba(blue100, 0.5);