@carbon-labs/react-animated-header 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/es/__stories__/AnimatedHeader.stories.d.ts +132 -4
  2. package/es/__stories__/data/index.d.ts +25 -0
  3. package/es/components/AnimatedHeader/AnimatedHeader.d.ts +4 -1
  4. package/es/components/AnimatedHeader/AnimatedHeader.js +72 -12
  5. package/es/components/ContentSwitcherSelector/ContentSwitcherSelector.d.ts +30 -0
  6. package/es/components/ContentSwitcherSelector/ContentSwitcherSelector.js +70 -0
  7. package/es/components/HeaderAction/HeaderAction.d.ts +15 -0
  8. package/es/components/HeaderAction/HeaderAction.js +69 -0
  9. package/es/components/HeaderAction/header-action.types.d.ts +32 -0
  10. package/es/components/HeaderTitle/HeaderTitle.js +2 -3
  11. package/es/components/TasksController/TasksController.d.ts +4 -1
  12. package/es/components/TasksController/TasksController.js +33 -23
  13. package/es/index.d.ts +2 -1
  14. package/es/index.js +1 -0
  15. package/lib/__stories__/AnimatedHeader.stories.d.ts +132 -4
  16. package/lib/__stories__/data/index.d.ts +25 -0
  17. package/lib/components/AnimatedHeader/AnimatedHeader.d.ts +4 -1
  18. package/lib/components/AnimatedHeader/AnimatedHeader.js +72 -12
  19. package/lib/components/ContentSwitcherSelector/ContentSwitcherSelector.d.ts +30 -0
  20. package/lib/components/ContentSwitcherSelector/ContentSwitcherSelector.js +74 -0
  21. package/lib/components/HeaderAction/HeaderAction.d.ts +15 -0
  22. package/lib/components/HeaderAction/HeaderAction.js +73 -0
  23. package/lib/components/HeaderAction/header-action.types.d.ts +32 -0
  24. package/lib/components/HeaderTitle/HeaderTitle.js +2 -3
  25. package/lib/components/TasksController/TasksController.d.ts +4 -1
  26. package/lib/components/TasksController/TasksController.js +33 -23
  27. package/lib/index.d.ts +2 -1
  28. package/lib/index.js +2 -0
  29. package/package.json +2 -2
  30. package/scss/AnimatedHeader/animated-header.scss +82 -0
  31. package/scss/HeaderAction/header-action.scss +54 -0
  32. package/scss/HeaderTitle/header-title.scss +18 -12
  33. package/scss/animated-header.scss +1 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright IBM Corp. 2024
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ Object.defineProperty(exports, '__esModule', { value: true });
11
+
12
+ var React = require('react');
13
+ var react = require('@carbon/react');
14
+ var usePrefix = require('../../node_modules/@carbon-labs/utilities/es/usePrefix.js');
15
+
16
+ const HeaderAction = ({
17
+ config,
18
+ headerExpanded
19
+ }) => {
20
+ const prefix = usePrefix.usePrefix();
21
+ const blockClass = `${prefix}--animated-header__header-action`;
22
+
23
+ // ICON
24
+ if (config.type === 'icon-button') {
25
+ const {
26
+ icon: Icon,
27
+ iconLabel,
28
+ onClick,
29
+ disabled,
30
+ ariaLabel
31
+ } = config;
32
+ return /*#__PURE__*/React.createElement("div", {
33
+ className: blockClass,
34
+ "aria-label": ariaLabel,
35
+ "aria-hidden": !headerExpanded,
36
+ "data-expanded": headerExpanded
37
+ }, /*#__PURE__*/React.createElement(react.IconButton, {
38
+ kind: "ghost",
39
+ size: "lg",
40
+ label: iconLabel,
41
+ onClick: onClick,
42
+ disabled: disabled
43
+ }, Icon && /*#__PURE__*/React.createElement(Icon, {
44
+ fill: `var(--cds-icon-secondary)`,
45
+ size: 16
46
+ })));
47
+ }
48
+
49
+ // GHOST
50
+ if (config.type === 'ghost-button') {
51
+ const {
52
+ label,
53
+ icon,
54
+ onClick,
55
+ disabled,
56
+ ariaLabel
57
+ } = config;
58
+ return /*#__PURE__*/React.createElement("div", {
59
+ className: blockClass,
60
+ "aria-label": ariaLabel,
61
+ "aria-hidden": !headerExpanded,
62
+ "data-expanded": headerExpanded
63
+ }, /*#__PURE__*/React.createElement(react.Button, {
64
+ kind: "ghost",
65
+ size: "lg",
66
+ onClick: onClick,
67
+ disabled: disabled,
68
+ renderIcon: icon
69
+ }, label));
70
+ }
71
+ };
72
+
73
+ exports.default = HeaderAction;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @license
3
+ *
4
+ * Copyright IBM Corp. 2025
5
+ *
6
+ * This source code is licensed under the Apache-2.0 license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ import { ElementType } from 'react';
10
+ type Base = {
11
+ ariaLabel?: string;
12
+ headerExpanded?: boolean;
13
+ };
14
+ export type HeaderActionIcon = Base & {
15
+ type: 'icon-button';
16
+ icon: ElementType;
17
+ iconLabel: string;
18
+ onClick: () => void;
19
+ disabled?: boolean;
20
+ };
21
+ export type HeaderActionGhost = Base & {
22
+ type: 'ghost-button';
23
+ label: string;
24
+ icon?: ElementType;
25
+ onClick: () => void;
26
+ disabled?: boolean;
27
+ };
28
+ export type HeaderActionConfig = HeaderActionIcon | HeaderActionGhost;
29
+ export type HeaderActionProps = {
30
+ headerActionConfig?: HeaderActionConfig | null;
31
+ };
32
+ export {};
@@ -35,13 +35,12 @@ const HeaderTitle = ({
35
35
  const blockClass = `${prefix}--animated-header__title`;
36
36
  const currentLang = typeof window !== 'undefined' ? document.documentElement.lang || 'en' : 'en';
37
37
  const isNameFirst = NAME_FIRST_LANGS.includes(currentLang.slice(0, 2));
38
- const headingCollapsed = `${blockClass}-collapsed`;
39
- const headingExpanded = `${blockClass}-expanded`;
40
38
  return /*#__PURE__*/React.createElement(react.Tooltip, {
41
39
  align: "bottom",
42
40
  label: `${welcomeText}, ${userName}`
43
41
  }, /*#__PURE__*/React.createElement("h1", {
44
- className: `${blockClass} ${headerExpanded ? headingExpanded : headingCollapsed}`,
42
+ className: blockClass,
43
+ "data-expanded": headerExpanded,
45
44
  "aria-label": ariaLabels?.welcome ?? `${welcomeText}, ${userName}`
46
45
  }, isNameFirst ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
47
46
  className: `${blockClass}-first`
@@ -17,6 +17,7 @@ export interface TasksControllerConfig {
17
17
  };
18
18
  dropdown?: {
19
19
  propsOverrides?: Partial<Omit<DropdownProps<TileGroup>, 'id' | 'items' | 'selectedItem'>>;
20
+ label?: string;
20
21
  ariaLabel?: string;
21
22
  };
22
23
  }
@@ -25,7 +26,9 @@ export type TasksControllerProps = {
25
26
  isLoading?: boolean;
26
27
  allTileGroups?: TileGroup[];
27
28
  selectedTileGroup?: TileGroup | null;
28
- setSelectedTileGroup: (e: any) => void;
29
+ setSelectedTileGroup?: (group: TileGroup | {
30
+ selectedItem: TileGroup;
31
+ }) => void;
29
32
  };
30
33
  declare const TasksController: ({ tasksControllerConfig, isLoading, allTileGroups, selectedTileGroup, setSelectedTileGroup, }: TasksControllerProps) => import("react/jsx-runtime").JSX.Element | null;
31
34
  export default TasksController;
@@ -10,8 +10,8 @@
10
10
  Object.defineProperty(exports, '__esModule', { value: true });
11
11
 
12
12
  var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
13
- var react = require('@carbon/react');
14
13
  var React = require('react');
14
+ var react = require('@carbon/react');
15
15
  var usePrefix = require('../../node_modules/@carbon-labs/utilities/es/usePrefix.js');
16
16
 
17
17
  const TasksController = ({
@@ -21,54 +21,64 @@ const TasksController = ({
21
21
  selectedTileGroup,
22
22
  setSelectedTileGroup
23
23
  }) => {
24
+ const prefix = usePrefix.usePrefix();
25
+ const blockClass = `${prefix}--animated-header`;
26
+
27
+ /** Button overrides */
24
28
  const {
25
29
  className: buttonCustomClass,
26
30
  ...buttonOverrideProps
27
31
  } = tasksControllerConfig?.button?.propsOverrides || {};
32
+
33
+ /** Dropdown overrides */
28
34
  const {
29
35
  className: dropdownCustomClass,
30
36
  onChange: dropdownCustomOnChange,
31
37
  ...dropdownOverrideProps
32
38
  } = tasksControllerConfig?.dropdown?.propsOverrides || {};
33
- const prefix = usePrefix.usePrefix();
34
- const blockClass = `${prefix}--animated-header`;
39
+
40
+ /** Early outs */
41
+ if (!tasksControllerConfig?.type) return null;
42
+ if (isLoading || tasksControllerConfig?.isLoading) {
43
+ return /*#__PURE__*/React.createElement(react.SkeletonPlaceholder, {
44
+ className: `${blockClass}__task-controller-skeleton`
45
+ });
46
+ }
47
+
48
+ /** Button mode */
49
+ if (tasksControllerConfig?.type === 'button' && tasksControllerConfig?.button?.text) {
50
+ return /*#__PURE__*/React.createElement(react.Button, _rollupPluginBabelHelpers.extends({
51
+ className: `${blockClass}__button${buttonCustomClass ? ` ${buttonCustomClass}` : ''}`
52
+ }, buttonOverrideProps), tasksControllerConfig.button.text);
53
+ }
54
+
55
+ /** Build Dropdown props (uses top-level list/selection/setter) */
35
56
  const dropdownProps = React.useMemo(() => {
36
- if (!allTileGroups?.length) {
37
- return null;
38
- }
57
+ if (!allTileGroups?.length) return null;
39
58
  return {
40
59
  id: `${blockClass}__header-dropdown`,
41
60
  className: `${blockClass}__header-dropdown${dropdownCustomClass ? ` ${dropdownCustomClass}` : ''}`,
42
61
  size: 'md',
43
62
  titleText: 'Label',
44
- label: allTileGroups[0]?.label ?? '',
63
+ label: tasksControllerConfig?.dropdown?.label ?? allTileGroups[0]?.label ?? '',
45
64
  hideLabel: true,
46
65
  type: 'inline',
47
66
  items: allTileGroups,
48
67
  selectedItem: selectedTileGroup ?? undefined,
49
68
  onChange: e => {
50
- setSelectedTileGroup?.(e);
69
+ if (e.selectedItem) {
70
+ setSelectedTileGroup?.({
71
+ selectedItem: e.selectedItem
72
+ });
73
+ }
51
74
  dropdownCustomOnChange?.(e);
52
75
  },
53
76
  'aria-label': tasksControllerConfig?.dropdown?.ariaLabel ?? 'Select a task group',
54
77
  ...dropdownOverrideProps
55
78
  };
56
- }, [allTileGroups, blockClass, dropdownCustomClass, selectedTileGroup, tasksControllerConfig?.dropdown?.ariaLabel, dropdownOverrideProps, setSelectedTileGroup, dropdownCustomOnChange]);
57
- if (!tasksControllerConfig?.type) {
58
- return null;
59
- }
60
- if (isLoading || tasksControllerConfig?.isLoading) {
61
- return /*#__PURE__*/React.createElement(react.SkeletonPlaceholder, {
62
- className: `${blockClass}__task-controller-skeleton`
63
- });
64
- }
79
+ }, [allTileGroups, selectedTileGroup, setSelectedTileGroup, blockClass, dropdownCustomClass, dropdownOverrideProps, dropdownCustomOnChange, tasksControllerConfig?.dropdown?.label, tasksControllerConfig?.dropdown?.ariaLabel]);
65
80
 
66
- // Button
67
- if (tasksControllerConfig?.type === 'button' && tasksControllerConfig?.button?.text) {
68
- return /*#__PURE__*/React.createElement(react.Button, _rollupPluginBabelHelpers.extends({
69
- className: `${blockClass}__button${buttonCustomClass ? ` ${buttonCustomClass}` : ''}`
70
- }, buttonOverrideProps), tasksControllerConfig.button.text);
71
- }
81
+ /** Dropdown mode */
72
82
  if (tasksControllerConfig?.type === 'dropdown' && dropdownProps) {
73
83
  return /*#__PURE__*/React.createElement("div", {
74
84
  className: `${blockClass}__header-dropdown--container`
package/lib/index.d.ts CHANGED
@@ -8,9 +8,10 @@
8
8
  */
9
9
  import AnimatedHeader from './components/AnimatedHeader/AnimatedHeader';
10
10
  import { AriaLabels, TileGroup } from './components/AnimatedHeader/types';
11
+ import HeaderAction from './components/HeaderAction/HeaderAction';
11
12
  import HeaderTitle from './components/HeaderTitle/HeaderTitle';
12
13
  import { BaseTile } from './components/Tiles/index';
13
14
  export * from './assets';
14
15
  export type { Workspace, WorkspaceSelectorConfig, } from './components/WorkspaceSelector/WorkspaceSelector';
15
16
  export type { TasksControllerConfig } from './components/TasksController/TasksController';
16
- export { AnimatedHeader, BaseTile, HeaderTitle, type AriaLabels, type TileGroup, };
17
+ export { AnimatedHeader, BaseTile, HeaderAction, HeaderTitle, type AriaLabels, type TileGroup, };
package/lib/index.js CHANGED
@@ -8,6 +8,7 @@
8
8
  'use strict';
9
9
 
10
10
  var AnimatedHeader = require('./components/AnimatedHeader/AnimatedHeader.js');
11
+ var HeaderAction = require('./components/HeaderAction/HeaderAction.js');
11
12
  var HeaderTitle = require('./components/HeaderTitle/HeaderTitle.js');
12
13
  require('react');
13
14
  require('@carbon/react');
@@ -37,6 +38,7 @@ var header_animated_wxbia_dark_06 = require('./assets/animated/header_animated_w
37
38
 
38
39
 
39
40
  exports.AnimatedHeader = AnimatedHeader.default;
41
+ exports.HeaderAction = HeaderAction.default;
40
42
  exports.HeaderTitle = HeaderTitle.default;
41
43
  exports.BaseTile = BaseTile.BaseTile;
42
44
  exports.dataFabricStaticLight = header_static_data_fabric_light_06.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbon-labs/react-animated-header",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -45,5 +45,5 @@
45
45
  "devDependencies": {
46
46
  "@carbon-labs/utilities": "canary"
47
47
  },
48
- "gitHead": "147eaebaa1e84f54d88fb94cb3338de1a16e8536"
48
+ "gitHead": "e309816781d0fc8aff80cb7c9525830cc20c753c"
49
49
  }
@@ -258,6 +258,80 @@ body {
258
258
  inline-size: 100%;
259
259
  }
260
260
 
261
+ .#{$prefix}__title-and-actions {
262
+ display: flex;
263
+ align-items: center;
264
+ justify-content: space-between;
265
+ gap: $spacing-07;
266
+ min-block-size: $spacing-09;
267
+ }
268
+
269
+ .#{$prefix}__actions {
270
+ display: flex;
271
+ flex-shrink: 0;
272
+ align-items: center;
273
+ }
274
+
275
+ .#{$prefix}__content-switcher--container {
276
+ position: relative;
277
+ z-index: 2;
278
+ display: inline-flex;
279
+ align-items: center;
280
+ inline-size: max-content;
281
+ isolation: isolate;
282
+
283
+ @media (prefers-reduced-motion: no-preference) {
284
+ transition: opacity 500ms linear,
285
+ margin 500ms motion.motion(standard, expressive);
286
+ }
287
+
288
+ &[data-expanded='true'] {
289
+ margin: $spacing-08 0 $spacing-05 0;
290
+ opacity: 1;
291
+ transition-delay: 110ms;
292
+ }
293
+
294
+ &[data-expanded='false'] {
295
+ margin: $spacing-02 0 $spacing-02 0;
296
+ line-height: 1.25;
297
+
298
+ opacity: 0;
299
+ transition-delay: 0ms;
300
+ }
301
+ }
302
+
303
+ .#{$prefix}__content-switcher--container::before {
304
+ position: absolute;
305
+ border-radius: inherit;
306
+ -webkit-backdrop-filter: blur(3px);
307
+ backdrop-filter: blur(3px);
308
+ content: '';
309
+ inset: 0;
310
+ pointer-events: none;
311
+ }
312
+
313
+ .#{$prefix}__content-switcher {
314
+ position: relative;
315
+ z-index: 1;
316
+
317
+ animation: 500ms motion.motion(standard, expressive) 0ms 1 slide-in;
318
+ animation-delay: 383.333ms;
319
+ animation-fill-mode: both;
320
+ }
321
+
322
+ .#{$prefix}__content-switcher-skeleton {
323
+ background: $layer-01;
324
+ block-size: $spacing-08;
325
+ inline-size: 27rem;
326
+ max-inline-size: 18rem;
327
+
328
+ opacity: 0.7;
329
+ }
330
+
331
+ .#{$prefix}__content-switcher-skeleton::before {
332
+ background: $layer-02;
333
+ }
334
+
261
335
  .#{$prefix}__content {
262
336
  z-index: 2;
263
337
  display: flex;
@@ -438,6 +512,10 @@ body {
438
512
  display: none;
439
513
  }
440
514
 
515
+ .#{$prefix}__header-switcher {
516
+ display: none;
517
+ }
518
+
441
519
  .#{$prefix}__content {
442
520
  display: none;
443
521
  }
@@ -457,6 +535,10 @@ body {
457
535
  display: block;
458
536
  }
459
537
 
538
+ .#{$prefix}__header-switcher {
539
+ display: inline-flex;
540
+ }
541
+
460
542
  .#{$prefix}__static {
461
543
  display: flex;
462
544
 
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @license
3
+ *
4
+ * Copyright IBM Corp. 2025
5
+ *
6
+ * This source code is licensed under the Apache-2.0 license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+
10
+ @use '@carbon/styles/scss/spacing' as *;
11
+ @use '@carbon/styles/scss/type' as *;
12
+ @use '@carbon/react/scss/theme' as *;
13
+
14
+ $prefix: 'clabs--animated-header__header-action' !default;
15
+
16
+ .#{$prefix} {
17
+ position: relative;
18
+ display: inline-flex;
19
+ align-items: center;
20
+ gap: $spacing-05;
21
+ min-block-size: 2.5rem;
22
+
23
+ &::after {
24
+ position: absolute;
25
+ z-index: 2;
26
+ display: block;
27
+ background-color: $border-subtle;
28
+ block-size: $spacing-06;
29
+ content: '';
30
+ inline-size: 0.0625rem;
31
+ inset-inline-end: 0;
32
+ }
33
+
34
+ button.cds--btn {
35
+ z-index: 3;
36
+ }
37
+
38
+ @media (prefers-reduced-motion: no-preference) {
39
+ transition: opacity 500ms linear, visibility 500ms linear;
40
+ }
41
+
42
+ &[data-expanded='true'] {
43
+ opacity: 1;
44
+ pointer-events: auto;
45
+ visibility: visible;
46
+ }
47
+
48
+ &[data-expanded='false'] {
49
+ opacity: 0;
50
+ pointer-events: none;
51
+ user-select: none;
52
+ visibility: hidden;
53
+ }
54
+ }
@@ -13,17 +13,30 @@
13
13
  $prefix: 'clabs--animated-header__title' !default;
14
14
 
15
15
  .#{$prefix} {
16
- @include type-style('heading-05');
17
-
18
16
  position: relative;
19
17
  z-index: 2;
20
18
  overflow: hidden;
21
- margin: $spacing-08 0 $spacing-05 0;
22
19
  text-overflow: ellipsis;
23
- transition: font-size 500ms motion.motion(standard, expressive),
24
- margin 500ms motion.motion(standard, expressive);
25
20
  white-space: nowrap;
26
21
 
22
+ @media (prefers-reduced-motion: no-preference) {
23
+ transition: font-size 500ms motion.motion(standard, expressive),
24
+ margin 500ms motion.motion(standard, expressive);
25
+ }
26
+
27
+ &[data-expanded='true'] {
28
+ @include type-style('heading-05');
29
+
30
+ margin: $spacing-08 0 $spacing-05 0;
31
+ }
32
+
33
+ &[data-expanded='false'] {
34
+ @include type-style('heading-03');
35
+
36
+ margin: calc($spacing-04 - 0.5px) 0 calc($spacing-04 - 0.5px) 0;
37
+ line-height: 1.25;
38
+ }
39
+
27
40
  @supports (-webkit-line-clamp: 3) {
28
41
  display: -webkit-box;
29
42
  overflow: hidden;
@@ -41,13 +54,6 @@ $prefix: 'clabs--animated-header__title' !default;
41
54
  }
42
55
  }
43
56
 
44
- .#{$prefix}-collapsed {
45
- @include type-style('heading-03');
46
-
47
- margin: $spacing-03 0 $spacing-04 0;
48
- line-height: 1.25;
49
- }
50
-
51
57
  .#{$prefix}-first {
52
58
  animation: 250ms ease-in fade-in;
53
59
  animation-fill-mode: both;
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  @use 'AnimatedHeader/animated-header.scss';
9
+ @use 'HeaderAction/header-action.scss';
9
10
  @use 'HeaderTitle/header-title.scss';
10
11
  @use 'Tiles/AITile/ai-tile.scss';
11
12
  @use 'Tiles/AIPromptTile/ai-prompt-tile.scss';