@carbon/ibm-products-web-components 0.41.1 → 0.42.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.
- package/custom-elements.json +64 -0
- package/es/components/coachmark/coachmark-body.d.ts +1 -1
- package/es/components/coachmark/coachmark-header.d.ts +1 -1
- package/es/components/coachmark/coachmark.d.ts +1 -1
- package/es/components/interstitial-screen/interstitial-screen-body-item.js +1 -1
- package/es/components/interstitial-screen/interstitial-screen-body.d.ts +1 -1
- package/es/components/interstitial-screen/interstitial-screen-footer.d.ts +1 -1
- package/es/components/interstitial-screen/interstitial-screen-footer.js +1 -1
- package/es/components/interstitial-screen/interstitial-screen-header.d.ts +1 -1
- package/es/components/interstitial-screen/interstitial-screen-header.js +1 -1
- package/es/components/interstitial-screen/interstitial-screen.d.ts +1 -1
- package/es/components/interstitial-screen/interstitial-screen.d.ts.map +1 -1
- package/es/components/interstitial-screen/interstitial-screen.js +2 -1
- package/es/components/interstitial-screen/interstitial-screen.js.map +1 -1
- package/es/components/page-header/context.d.ts +1 -0
- package/es/components/page-header/context.d.ts.map +1 -1
- package/es/components/page-header/context.js +2 -1
- package/es/components/page-header/context.js.map +1 -1
- package/es/components/page-header/page-header-breadcrumb.d.ts +3 -0
- package/es/components/page-header/page-header-breadcrumb.d.ts.map +1 -1
- package/es/components/page-header/page-header-breadcrumb.js +18 -3
- package/es/components/page-header/page-header-breadcrumb.js.map +1 -1
- package/es/components/page-header/page-header-tabs.d.ts +7 -0
- package/es/components/page-header/page-header-tabs.d.ts.map +1 -1
- package/es/components/page-header/page-header-tabs.js +27 -0
- package/es/components/page-header/page-header-tabs.js.map +1 -1
- package/es/components/page-header/page-header.d.ts +2 -0
- package/es/components/page-header/page-header.d.ts.map +1 -1
- package/es/components/page-header/page-header.js +5 -0
- package/es/components/page-header/page-header.js.map +1 -1
- package/es/components/page-header/page-header.scss.js +1 -1
- package/es/components/page-header/page-header.scss.js.map +1 -1
- package/es/components/side-panel/side-panel.d.ts +31 -0
- package/es/components/side-panel/side-panel.d.ts.map +1 -1
- package/es/components/side-panel/side-panel.js +89 -0
- package/es/components/side-panel/side-panel.js.map +1 -1
- package/es/components/side-panel/side-panel.scss.js +1 -1
- package/es/components/side-panel/side-panel.scss.js.map +1 -1
- package/es/components/tearsheet/tearsheet.scss.js +1 -1
- package/es/components/tearsheet-preview/tearsheet-body.d.ts +1 -1
- package/es/components/tearsheet-preview/tearsheet-footer.d.ts +6 -2
- package/es/components/tearsheet-preview/tearsheet-footer.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-footer.js +15 -1
- package/es/components/tearsheet-preview/tearsheet-footer.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-header-content.d.ts +8 -0
- package/es/components/tearsheet-preview/tearsheet-header-content.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-header-content.js +12 -1
- package/es/components/tearsheet-preview/tearsheet-header-content.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-header-content.scss.js +1 -1
- package/es/components/tearsheet-preview/tearsheet-header.d.ts +1 -1
- package/es/components/tearsheet-preview/tearsheet-header.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-header.js +12 -4
- package/es/components/tearsheet-preview/tearsheet-header.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-header.scss.js +1 -1
- package/es/components/tearsheet-preview/tearsheet-influencer.d.ts +5 -1
- package/es/components/tearsheet-preview/tearsheet-influencer.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-influencer.js +4 -0
- package/es/components/tearsheet-preview/tearsheet-influencer.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-navigation-bar.scss.js +1 -1
- package/es/components/tearsheet-preview/tearsheet-signal.d.ts +1 -0
- package/es/components/tearsheet-preview/tearsheet-signal.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-signal.js +2 -1
- package/es/components/tearsheet-preview/tearsheet-signal.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-summary-content.d.ts +5 -1
- package/es/components/tearsheet-preview/tearsheet-summary-content.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet-summary-content.js +4 -0
- package/es/components/tearsheet-preview/tearsheet-summary-content.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet.d.ts +11 -1
- package/es/components/tearsheet-preview/tearsheet.d.ts.map +1 -1
- package/es/components/tearsheet-preview/tearsheet.js +30 -1
- package/es/components/tearsheet-preview/tearsheet.js.map +1 -1
- package/es/components/tearsheet-preview/tearsheet.scss.js +1 -1
- package/es/components/tearsheet-preview/tearsheet.scss.js.map +1 -1
- package/es/ibm-products-web-components/package.js +1 -1
- package/es/utilities/manageFocusTrap/manageFocusTrap.d.ts +121 -3
- package/es/utilities/manageFocusTrap/manageFocusTrap.d.ts.map +1 -1
- package/es/utilities/manageFocusTrap/manageFocusTrap.js +188 -33
- package/es/utilities/manageFocusTrap/manageFocusTrap.js.map +1 -1
- package/es-custom/components/interstitial-screen/interstitial-screen-body-item.js +1 -1
- package/es-custom/components/interstitial-screen/interstitial-screen-footer.js +1 -1
- package/es-custom/components/interstitial-screen/interstitial-screen-header.js +1 -1
- package/es-custom/components/interstitial-screen/interstitial-screen.js +2 -1
- package/es-custom/components/interstitial-screen/interstitial-screen.js.map +1 -1
- package/es-custom/components/page-header/context.js +2 -1
- package/es-custom/components/page-header/context.js.map +1 -1
- package/es-custom/components/page-header/page-header-breadcrumb.js +18 -3
- package/es-custom/components/page-header/page-header-breadcrumb.js.map +1 -1
- package/es-custom/components/page-header/page-header-tabs.js +27 -0
- package/es-custom/components/page-header/page-header-tabs.js.map +1 -1
- package/es-custom/components/page-header/page-header.js +5 -0
- package/es-custom/components/page-header/page-header.js.map +1 -1
- package/es-custom/components/page-header/page-header.scss.js +1 -1
- package/es-custom/components/page-header/page-header.scss.js.map +1 -1
- package/es-custom/components/side-panel/side-panel.js +89 -0
- package/es-custom/components/side-panel/side-panel.js.map +1 -1
- package/es-custom/components/side-panel/side-panel.scss.js +1 -1
- package/es-custom/components/side-panel/side-panel.scss.js.map +1 -1
- package/es-custom/components/tearsheet/tearsheet.scss.js +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-footer.js +15 -1
- package/es-custom/components/tearsheet-preview/tearsheet-footer.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-header-content.js +12 -1
- package/es-custom/components/tearsheet-preview/tearsheet-header-content.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-header-content.scss.js +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-header.js +12 -4
- package/es-custom/components/tearsheet-preview/tearsheet-header.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-header.scss.js +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-influencer.js +4 -0
- package/es-custom/components/tearsheet-preview/tearsheet-influencer.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-navigation-bar.scss.js +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-signal.js +2 -1
- package/es-custom/components/tearsheet-preview/tearsheet-signal.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet-summary-content.js +4 -0
- package/es-custom/components/tearsheet-preview/tearsheet-summary-content.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet.js +30 -1
- package/es-custom/components/tearsheet-preview/tearsheet.js.map +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet.scss.js +1 -1
- package/es-custom/components/tearsheet-preview/tearsheet.scss.js.map +1 -1
- package/es-custom/ibm-products-web-components/package.js +1 -1
- package/es-custom/utilities/manageFocusTrap/manageFocusTrap.js +188 -33
- package/es-custom/utilities/manageFocusTrap/manageFocusTrap.js.map +1 -1
- package/lib/components/coachmark/coachmark-body.d.ts +1 -1
- package/lib/components/coachmark/coachmark-header.d.ts +1 -1
- package/lib/components/coachmark/coachmark.d.ts +1 -1
- package/lib/components/interstitial-screen/interstitial-screen-body.d.ts +1 -1
- package/lib/components/interstitial-screen/interstitial-screen-footer.d.ts +1 -1
- package/lib/components/interstitial-screen/interstitial-screen-header.d.ts +1 -1
- package/lib/components/interstitial-screen/interstitial-screen.d.ts +1 -1
- package/lib/components/interstitial-screen/interstitial-screen.d.ts.map +1 -1
- package/lib/components/page-header/context.d.ts +1 -0
- package/lib/components/page-header/context.d.ts.map +1 -1
- package/lib/components/page-header/page-header-breadcrumb.d.ts +3 -0
- package/lib/components/page-header/page-header-breadcrumb.d.ts.map +1 -1
- package/lib/components/page-header/page-header-tabs.d.ts +7 -0
- package/lib/components/page-header/page-header-tabs.d.ts.map +1 -1
- package/lib/components/page-header/page-header.d.ts +2 -0
- package/lib/components/page-header/page-header.d.ts.map +1 -1
- package/lib/components/side-panel/side-panel.d.ts +31 -0
- package/lib/components/side-panel/side-panel.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-body.d.ts +1 -1
- package/lib/components/tearsheet-preview/tearsheet-footer.d.ts +6 -2
- package/lib/components/tearsheet-preview/tearsheet-footer.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-header-content.d.ts +8 -0
- package/lib/components/tearsheet-preview/tearsheet-header-content.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-header.d.ts +1 -1
- package/lib/components/tearsheet-preview/tearsheet-header.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-influencer.d.ts +5 -1
- package/lib/components/tearsheet-preview/tearsheet-influencer.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-signal.d.ts +1 -0
- package/lib/components/tearsheet-preview/tearsheet-signal.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet-summary-content.d.ts +5 -1
- package/lib/components/tearsheet-preview/tearsheet-summary-content.d.ts.map +1 -1
- package/lib/components/tearsheet-preview/tearsheet.d.ts +11 -1
- package/lib/components/tearsheet-preview/tearsheet.d.ts.map +1 -1
- package/lib/utilities/manageFocusTrap/manageFocusTrap.d.ts +121 -3
- package/lib/utilities/manageFocusTrap/manageFocusTrap.d.ts.map +1 -1
- package/package.json +7 -6
- package/scss/components/page-header/page-header.scss +49 -10
- package/scss/components/side-panel/side-panel.scss +19 -0
- package/scss/components/tearsheet-preview/tearsheet.scss +5 -0
- package/telemetry.yml +5 -0
|
@@ -16,25 +16,161 @@ import { prefix, selectorTabbable } from "@carbon/web-components/es/globals/sett
|
|
|
16
16
|
* LICENSE file in the root directory of this source tree.
|
|
17
17
|
*/
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @returns {object} An object with a `cleanup` method to remove event listeners.
|
|
19
|
+
* Simple object to store containers by uniqueId
|
|
20
|
+
* { id: [container1, container2, ...] }
|
|
22
21
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const containers = {};
|
|
23
|
+
/**
|
|
24
|
+
* Default key for backward compatibility when no uniqueId is provided
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_KEY = "__default__";
|
|
27
|
+
/**
|
|
28
|
+
* Registers a focusable container to be included in the focus trap.
|
|
29
|
+
*
|
|
30
|
+
* This function allows child components to register their shadow roots or DOM elements
|
|
31
|
+
* so that the focus trap utility can query focusable elements within them. This is necessary
|
|
32
|
+
* because shadow DOM boundaries prevent direct querying from parent components.
|
|
33
|
+
*
|
|
34
|
+
* @param container - The HTMLElement or ShadowRoot to register as a focusable container.
|
|
35
|
+
* If null, the function returns early without registering.
|
|
36
|
+
* @param uniqueId - Optional unique identifier to scope containers to a specific component instance.
|
|
37
|
+
* If not provided, uses a default key for backward compatibility.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // In a child component's firstUpdated lifecycle
|
|
41
|
+
* protected firstUpdated() {
|
|
42
|
+
* const uniqueId = tearsheetSignal.get().uniqueId;
|
|
43
|
+
* registerFocusableContainers(this.shadowRoot, uniqueId);
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Without uniqueId (uses default key)
|
|
48
|
+
* registerFocusableContainers(this.shadowRoot);
|
|
49
|
+
*/
|
|
50
|
+
const registerFocusableContainers = (container, uniqueId) => {
|
|
51
|
+
if (!container) return;
|
|
52
|
+
const id = uniqueId || DEFAULT_KEY;
|
|
53
|
+
if (!containers[id]) containers[id] = [];
|
|
54
|
+
if (!containers[id].includes(container)) containers[id].push(container);
|
|
26
55
|
};
|
|
27
|
-
|
|
28
|
-
|
|
56
|
+
/**
|
|
57
|
+
* UnRegisters a previously registered focusable container.
|
|
58
|
+
*
|
|
59
|
+
* This function should be called in the component's disconnectedCallback lifecycle method
|
|
60
|
+
* to clean up and prevent memory leaks when the component is removed from the DOM.
|
|
61
|
+
*
|
|
62
|
+
* @param container - The HTMLElement or ShadowRoot to unregister.
|
|
63
|
+
* If null, the function returns early without doing anything.
|
|
64
|
+
* @param uniqueId - Optional unique identifier that was used when registering the container.
|
|
65
|
+
* If not provided, uses the default key.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // In a child component's disconnectedCallback
|
|
69
|
+
* disconnectedCallback() {
|
|
70
|
+
* super.disconnectedCallback();
|
|
71
|
+
* const uniqueId = tearsheetSignal.get().uniqueId;
|
|
72
|
+
* unregisterFocusableContainers(this.shadowRoot, uniqueId);
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // Without uniqueId (uses default key)
|
|
77
|
+
* disconnectedCallback() {
|
|
78
|
+
* super.disconnectedCallback();
|
|
79
|
+
* unregisterFocusableContainers(this.shadowRoot);
|
|
80
|
+
* }
|
|
81
|
+
*/
|
|
82
|
+
const unregisterFocusableContainers = (container, uniqueId) => {
|
|
83
|
+
if (!container) return;
|
|
84
|
+
const id = uniqueId || DEFAULT_KEY;
|
|
85
|
+
if (containers[id]) {
|
|
86
|
+
containers[id] = containers[id].filter((c) => c !== container);
|
|
87
|
+
if (containers[id].length === 0) delete containers[id];
|
|
88
|
+
}
|
|
29
89
|
};
|
|
30
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Clears all registered focusable containers, either globally or for a specific uniqueId.
|
|
92
|
+
*
|
|
93
|
+
* This function is typically called when a parent component (like a tearsheet or modal) is closing
|
|
94
|
+
* or being removed from the DOM. It ensures all registered containers are cleaned up to prevent
|
|
95
|
+
* memory leaks.
|
|
96
|
+
*
|
|
97
|
+
* @param uniqueId - Optional unique identifier to clear containers for a specific component instance.
|
|
98
|
+
* If provided, only containers registered with this uniqueId are cleared.
|
|
99
|
+
* If not provided, ALL containers across all uniqueIds are cleared.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Clear containers for a specific tearsheet instance
|
|
103
|
+
* clearFocusableContainers(this.uniqueId);
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Clear all containers (typically in disconnectedCallback)
|
|
107
|
+
* disconnectedCallback() {
|
|
108
|
+
* super.disconnectedCallback();
|
|
109
|
+
* this._trapFocusAPI?.cleanup();
|
|
110
|
+
* clearFocusableContainers();
|
|
111
|
+
* }
|
|
112
|
+
*/
|
|
113
|
+
const clearFocusableContainers = (uniqueId) => {
|
|
114
|
+
if (uniqueId) delete containers[uniqueId];
|
|
115
|
+
else Object.keys(containers).forEach((key) => delete containers[key]);
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Traps keyboard focus within registered focusable containers.
|
|
119
|
+
*
|
|
120
|
+
* This function creates a focus trap that prevents Tab/Shift+Tab navigation from leaving
|
|
121
|
+
* the registered containers. When the user reaches the last focusable element and presses Tab,
|
|
122
|
+
* focus wraps to the first element, and vice versa with Shift+Tab.
|
|
123
|
+
*
|
|
124
|
+
* The function works with Shadow DOM by querying focusable elements from all registered
|
|
125
|
+
* containers, which can include shadow roots from child components.
|
|
126
|
+
*
|
|
127
|
+
* @param wrapper - Optional wrapper element to attach the keydown listener to.
|
|
128
|
+
* If not provided, defaults to document for backward compatibility.
|
|
129
|
+
* Using a specific wrapper can improve performance and scope the listener.
|
|
130
|
+
* @param uniqueId - Optional unique identifier to scope the focus trap to specific containers.
|
|
131
|
+
* If provided, only containers registered with this uniqueId are included.
|
|
132
|
+
* If not provided, uses the default key for backward compatibility.
|
|
133
|
+
*
|
|
134
|
+
* @returns An object with a `cleanup` method that removes event listeners.
|
|
135
|
+
* Call this method when the component is closing or unmounting.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // In a parent component when opening
|
|
139
|
+
* protected updated(changedProps) {
|
|
140
|
+
* if (changedProps.has('open') && this.open) {
|
|
141
|
+
* // Update signal so children can register
|
|
142
|
+
* updateTearsheetSignals({ uniqueId: this.uniqueId });
|
|
143
|
+
*
|
|
144
|
+
* // Wait for children to register, then trap focus
|
|
145
|
+
* requestAnimationFrame(() => {
|
|
146
|
+
* this._trapFocusAPI = trapFocus(this as HTMLElement, this.uniqueId);
|
|
147
|
+
* });
|
|
148
|
+
* }
|
|
149
|
+
* }
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Without uniqueId (uses default key)
|
|
153
|
+
* this._trapFocusAPI = trapFocus();
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* // Cleanup when closing
|
|
157
|
+
* disconnectedCallback() {
|
|
158
|
+
* this._trapFocusAPI?.cleanup();
|
|
159
|
+
* clearFocusableContainers(this.uniqueId);
|
|
160
|
+
* }
|
|
161
|
+
*/
|
|
162
|
+
const trapFocus = (wrapper, uniqueId) => {
|
|
31
163
|
const selectorTabbable$1 = selectorTabbable.replace(`${prefix}-button`, `${prefix}-button:not([disabled]), ${prefix}-selectable-tag`);
|
|
32
|
-
|
|
33
|
-
if (!
|
|
164
|
+
const containerArray = containers[uniqueId || DEFAULT_KEY];
|
|
165
|
+
if (!containerArray || containerArray.length === 0) return { cleanup: () => {} };
|
|
166
|
+
const eventTarget = wrapper || document;
|
|
34
167
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* -
|
|
168
|
+
* Determines the best focusable element within a given container.
|
|
169
|
+
*
|
|
170
|
+
* @param el - The element to check for ability to focus
|
|
171
|
+
* @returns The focusable element, or null if none found
|
|
172
|
+
*
|
|
173
|
+
* @internal
|
|
38
174
|
*/
|
|
39
175
|
const getFocusTarget = (el) => {
|
|
40
176
|
if (!el) return null;
|
|
@@ -42,21 +178,29 @@ const trapFocus = (containers) => {
|
|
|
42
178
|
return el.querySelector("button, [tabindex]:not([tabindex=\"-1\"])") || el;
|
|
43
179
|
};
|
|
44
180
|
/**
|
|
45
|
-
|
|
46
|
-
* Returns the "real" active element in the document, resolving through
|
|
47
|
-
* Shadow DOM and slot boundaries when necessary.
|
|
181
|
+
* Returns the actual focused element, resolving through Shadow DOM and slot boundaries.
|
|
48
182
|
*
|
|
49
|
-
* In
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
183
|
+
* In standard DOM, `document.activeElement` returns the focused element. However, with
|
|
184
|
+
* Shadow DOM and slots, this can be misleading:
|
|
185
|
+
* - If focus is inside a shadow root, `document.activeElement` returns the host element,
|
|
186
|
+
* not the actual focused element inside the shadow root.
|
|
187
|
+
* - If focus is on a `<slot>` element, we need to resolve to the assigned element.
|
|
188
|
+
*
|
|
189
|
+
* This function traverses shadow roots to find the true focused element, stopping at
|
|
190
|
+
* tabbable elements like buttons to avoid going too deep into component internals.
|
|
191
|
+
*
|
|
192
|
+
* @returns The actual focused element in the document
|
|
193
|
+
*
|
|
194
|
+
* @internal
|
|
56
195
|
*/
|
|
57
196
|
const getRealActiveElement = () => {
|
|
58
197
|
let activeElement = document.activeElement;
|
|
59
|
-
|
|
198
|
+
while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) {
|
|
199
|
+
const nextElement = activeElement.shadowRoot.activeElement;
|
|
200
|
+
const tagName = activeElement.tagName.toLowerCase();
|
|
201
|
+
if (selectorTabbable$1.indexOf(tagName) !== -1 || tagName.includes("-button") || activeElement.hasAttribute("tabindex")) break;
|
|
202
|
+
activeElement = nextElement;
|
|
203
|
+
}
|
|
60
204
|
if (activeElement?.tagName === "SLOT") {
|
|
61
205
|
let assigned;
|
|
62
206
|
if (activeElement instanceof HTMLSlotElement) assigned = activeElement.assignedElements({ flatten: true });
|
|
@@ -66,11 +210,18 @@ const trapFocus = (containers) => {
|
|
|
66
210
|
return activeElement;
|
|
67
211
|
};
|
|
68
212
|
requestAnimationFrame(() => {
|
|
69
|
-
getFocusTarget(getAllFocusableElements(
|
|
213
|
+
getFocusTarget(getAllFocusableElements(containerArray)[0])?.focus({ preventScroll: true });
|
|
70
214
|
});
|
|
215
|
+
/**
|
|
216
|
+
* Handles Tab and Shift+Tab key presses to trap focus within containers.
|
|
217
|
+
*
|
|
218
|
+
* @param e - The keyboard event
|
|
219
|
+
*
|
|
220
|
+
* @internal
|
|
221
|
+
*/
|
|
71
222
|
function onKeyDown(e) {
|
|
72
223
|
if (e.key !== "Tab") return;
|
|
73
|
-
const elements = getAllFocusableElements(
|
|
224
|
+
const elements = getAllFocusableElements(containerArray);
|
|
74
225
|
const first = elements[0];
|
|
75
226
|
const last = elements[elements.length - 1];
|
|
76
227
|
const active = getRealActiveElement();
|
|
@@ -85,16 +236,20 @@ const trapFocus = (containers) => {
|
|
|
85
236
|
}
|
|
86
237
|
}
|
|
87
238
|
/**
|
|
88
|
-
* Retrieves all focusable elements
|
|
89
|
-
|
|
239
|
+
* Retrieves all focusable elements from all registered containers.
|
|
240
|
+
*
|
|
241
|
+
* @param containers - Array of containers to query for focusable elements
|
|
242
|
+
* @returns Array of all focusable elements found
|
|
243
|
+
*
|
|
244
|
+
* @internal
|
|
245
|
+
*/
|
|
90
246
|
const getAllFocusableElements = (containers) => containers.flatMap((container) => [...container.querySelectorAll(selectorTabbable$1)]).filter(Boolean);
|
|
91
|
-
|
|
247
|
+
eventTarget.addEventListener("keydown", onKeyDown, true);
|
|
92
248
|
return { cleanup: () => {
|
|
93
|
-
|
|
94
|
-
_containers.length = 0;
|
|
249
|
+
eventTarget.removeEventListener("keydown", onKeyDown, true);
|
|
95
250
|
} };
|
|
96
251
|
};
|
|
97
252
|
//#endregion
|
|
98
|
-
export { clearFocusableContainers, registerFocusableContainers, trapFocus };
|
|
253
|
+
export { clearFocusableContainers, registerFocusableContainers, trapFocus, unregisterFocusableContainers };
|
|
99
254
|
|
|
100
255
|
//# sourceMappingURL=manageFocusTrap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manageFocusTrap.js","names":["selectorTabbable","selectorTabbableCarbon","carbonPrefix"],"sources":["../../../src/utilities/manageFocusTrap/manageFocusTrap.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {\n selectorTabbable as selectorTabbableCarbon,\n prefix as carbonPrefix,\n} from '@carbon/web-components/es/globals/settings';\n/**\n * Traps focus within a set of containers, ensuring it stays within focusable elements.\n * @param {NodeListOf<Element>} containers - The containers to trap focus within.\n * @returns {object} An object with a `cleanup` method to remove event listeners.\n */\nlet _containers: HTMLElement[] = [];\n\nexport const registerFocusableContainers = (container) => {\n if (container) {\n _containers.push(container);\n }\n};\nexport const clearFocusableContainers = () => {\n _containers.length = 0;\n};\nexport const trapFocus = (containers?: HTMLElement[]) => {\n const selectorTabbable = selectorTabbableCarbon.replace(\n `${carbonPrefix}-button`,\n `${carbonPrefix}-button:not([disabled]), ${carbonPrefix}-selectable-tag`\n );\n\n if (containers && containers.length > 0) {\n _containers = containers;\n }\n if (!_containers.length) {\n return { cleanup: () => {} };\n }\n\n /**\n * Given a container element, return the best candidate to focus.\n * - If element itself is tabbable, return it.\n * - Otherwise, find the first tabbable descendant.\n */\n const getFocusTarget = (el) => {\n if (!el) {\n return null;\n }\n if (el.tagName === 'BUTTON' || el.tabIndex >= 0) {\n return el;\n }\n const inner = el.querySelector('button, [tabindex]:not([tabindex=\"-1\"])');\n return inner || el;\n };\n\n /**\n\n * Returns the \"real\" active element in the document, resolving through\n * Shadow DOM and slot boundaries when necessary.\n *\n * In some cases `document.activeElement` may not reflect the actual\n * tabbable node that has focus:\n * - If focus is inside a shadow root, `document.activeElement` is the host,\n * not the element inside. This utility dives into the shadowRoot to find\n * the inner active element.\n * - If focus is on a `<slot>` element, this function resolves to the first\n * tabbable assigned element within the slot.\n */\n const getRealActiveElement = () => {\n let activeElement = document.activeElement;\n\n // If inside shadow DOM\n if (\n activeElement &&\n selectorTabbable.indexOf(activeElement.tagName.toLocaleLowerCase()) ==\n -1 &&\n activeElement?.shadowRoot?.activeElement\n ) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n\n // If active element is a slot\n if (activeElement?.tagName === 'SLOT') {\n let assigned: Element[] | undefined;\n\n if (activeElement instanceof HTMLSlotElement) {\n assigned = activeElement.assignedElements({ flatten: true });\n }\n const candidate = assigned?.find((node) =>\n node.matches('button, [tabindex]:not([tabindex=\"-1\"])')\n );\n if (candidate) {\n activeElement = candidate;\n }\n }\n\n return activeElement;\n };\n\n //Optionally focus first element immediately\n requestAnimationFrame(() => {\n const elements = getAllFocusableElements(_containers);\n getFocusTarget(elements[0])?.focus({ preventScroll: true });\n });\n\n function onKeyDown(e) {\n if (e.key !== 'Tab') {\n return;\n }\n\n // Flatten all focusable elements from all containers\n const elements = getAllFocusableElements(_containers);\n\n const first = elements[0];\n const last = elements[elements.length - 1];\n const active = getRealActiveElement();\n\n if (e.shiftKey) {\n // Shift+Tab wrap\n if (active === first || first.contains(active)) {\n e.preventDefault();\n getFocusTarget(last)?.focus();\n }\n } else {\n // Tab wrap\n if (active === last || last.contains(active)) {\n e.preventDefault();\n getFocusTarget(first)?.focus();\n }\n }\n }\n\n /**\n * Retrieves all focusable elements within the provided containers.\n **/\n const getAllFocusableElements = (containers) =>\n containers\n .flatMap((container) => [...container.querySelectorAll(selectorTabbable)])\n .filter(Boolean);\n\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n cleanup: () => {\n document.removeEventListener('keydown', onKeyDown, true);\n _containers.length = 0;\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAkBA,IAAI,cAA6B,EAAE;AAEnC,MAAa,+BAA+B,cAAc;AACxD,KAAI,UACF,aAAY,KAAK,UAAU;;AAG/B,MAAa,iCAAiC;AAC5C,aAAY,SAAS;;AAEvB,MAAa,aAAa,eAA+B;CACvD,MAAMA,qBAAmBC,iBAAuB,QAC9C,GAAGC,OAAa,UAChB,GAAGA,OAAa,2BAA2BA,OAAa,iBACzD;AAED,KAAI,cAAc,WAAW,SAAS,EACpC,eAAc;AAEhB,KAAI,CAAC,YAAY,OACf,QAAO,EAAE,eAAe,IAAI;;;;;;CAQ9B,MAAM,kBAAkB,OAAO;AAC7B,MAAI,CAAC,GACH,QAAO;AAET,MAAI,GAAG,YAAY,YAAY,GAAG,YAAY,EAC5C,QAAO;AAGT,SADc,GAAG,cAAc,4CACnB,IAAI;;;;;;;;;;;;;;;CAgBlB,MAAM,6BAA6B;EACjC,IAAI,gBAAgB,SAAS;AAG7B,MACE,iBACAF,mBAAiB,QAAQ,cAAc,QAAQ,mBAAmB,CAAC,IACjE,MACF,eAAe,YAAY,cAE3B,iBAAgB,cAAc,WAAW;AAI3C,MAAI,eAAe,YAAY,QAAQ;GACrC,IAAI;AAEJ,OAAI,yBAAyB,gBAC3B,YAAW,cAAc,iBAAiB,EAAE,SAAS,MAAM,CAAC;GAE9D,MAAM,YAAY,UAAU,MAAM,SAChC,KAAK,QAAQ,4CAA0C,CACxD;AACD,OAAI,UACF,iBAAgB;;AAIpB,SAAO;;AAIT,6BAA4B;AAE1B,iBADiB,wBAAwB,YAClB,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,MAAM,CAAC;GAC3D;CAEF,SAAS,UAAU,GAAG;AACpB,MAAI,EAAE,QAAQ,MACZ;EAIF,MAAM,WAAW,wBAAwB,YAAY;EAErD,MAAM,QAAQ,SAAS;EACvB,MAAM,OAAO,SAAS,SAAS,SAAS;EACxC,MAAM,SAAS,sBAAsB;AAErC,MAAI,EAAE;OAEA,WAAW,SAAS,MAAM,SAAS,OAAO,EAAE;AAC9C,MAAE,gBAAgB;AAClB,mBAAe,KAAK,EAAE,OAAO;;aAI3B,WAAW,QAAQ,KAAK,SAAS,OAAO,EAAE;AAC5C,KAAE,gBAAgB;AAClB,kBAAe,MAAM,EAAE,OAAO;;;;;;CAQpC,MAAM,2BAA2B,eAC/B,WACG,SAAS,cAAc,CAAC,GAAG,UAAU,iBAAiBA,mBAAiB,CAAC,CAAC,CACzE,OAAO,QAAQ;AAEpB,UAAS,iBAAiB,WAAW,WAAW,KAAK;AAErD,QAAO,EACL,eAAe;AACb,WAAS,oBAAoB,WAAW,WAAW,KAAK;AACxD,cAAY,SAAS;IAExB"}
|
|
1
|
+
{"version":3,"file":"manageFocusTrap.js","names":["selectorTabbable","selectorTabbableCarbon","carbonPrefix"],"sources":["../../../src/utilities/manageFocusTrap/manageFocusTrap.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {\n selectorTabbable as selectorTabbableCarbon,\n prefix as carbonPrefix,\n} from '@carbon/web-components/es/globals/settings';\n\n/**\n * Simple object to store containers by uniqueId\n * { id: [container1, container2, ...] }\n */\nconst containers: Record<string, Array<HTMLElement | ShadowRoot>> = {};\n\n/**\n * Default key for backward compatibility when no uniqueId is provided\n */\nconst DEFAULT_KEY = '__default__';\n\n/**\n * Registers a focusable container to be included in the focus trap.\n *\n * This function allows child components to register their shadow roots or DOM elements\n * so that the focus trap utility can query focusable elements within them. This is necessary\n * because shadow DOM boundaries prevent direct querying from parent components.\n *\n * @param container - The HTMLElement or ShadowRoot to register as a focusable container.\n * If null, the function returns early without registering.\n * @param uniqueId - Optional unique identifier to scope containers to a specific component instance.\n * If not provided, uses a default key for backward compatibility.\n *\n * @example\n * // In a child component's firstUpdated lifecycle\n * protected firstUpdated() {\n * const uniqueId = tearsheetSignal.get().uniqueId;\n * registerFocusableContainers(this.shadowRoot, uniqueId);\n * }\n *\n * @example\n * // Without uniqueId (uses default key)\n * registerFocusableContainers(this.shadowRoot);\n */\nexport const registerFocusableContainers = (\n container: HTMLElement | ShadowRoot | null,\n uniqueId?: string\n) => {\n if (!container) {\n return;\n }\n\n const id = uniqueId || DEFAULT_KEY;\n\n // Initialize array if it doesn't exist\n if (!containers[id]) {\n containers[id] = [];\n }\n\n // Add container if not already present\n if (!containers[id].includes(container)) {\n containers[id].push(container);\n }\n};\n\n/**\n * UnRegisters a previously registered focusable container.\n *\n * This function should be called in the component's disconnectedCallback lifecycle method\n * to clean up and prevent memory leaks when the component is removed from the DOM.\n *\n * @param container - The HTMLElement or ShadowRoot to unregister.\n * If null, the function returns early without doing anything.\n * @param uniqueId - Optional unique identifier that was used when registering the container.\n * If not provided, uses the default key.\n *\n * @example\n * // In a child component's disconnectedCallback\n * disconnectedCallback() {\n * super.disconnectedCallback();\n * const uniqueId = tearsheetSignal.get().uniqueId;\n * unregisterFocusableContainers(this.shadowRoot, uniqueId);\n * }\n *\n * @example\n * // Without uniqueId (uses default key)\n * disconnectedCallback() {\n * super.disconnectedCallback();\n * unregisterFocusableContainers(this.shadowRoot);\n * }\n */\nexport const unregisterFocusableContainers = (\n container: HTMLElement | ShadowRoot | null,\n uniqueId?: string\n) => {\n if (!container) {\n return;\n }\n\n const id = uniqueId || DEFAULT_KEY;\n if (containers[id]) {\n containers[id] = containers[id].filter((c) => c !== container);\n if (containers[id].length === 0) {\n delete containers[id];\n }\n }\n};\n\n/**\n * Clears all registered focusable containers, either globally or for a specific uniqueId.\n *\n * This function is typically called when a parent component (like a tearsheet or modal) is closing\n * or being removed from the DOM. It ensures all registered containers are cleaned up to prevent\n * memory leaks.\n *\n * @param uniqueId - Optional unique identifier to clear containers for a specific component instance.\n * If provided, only containers registered with this uniqueId are cleared.\n * If not provided, ALL containers across all uniqueIds are cleared.\n *\n * @example\n * // Clear containers for a specific tearsheet instance\n * clearFocusableContainers(this.uniqueId);\n *\n * @example\n * // Clear all containers (typically in disconnectedCallback)\n * disconnectedCallback() {\n * super.disconnectedCallback();\n * this._trapFocusAPI?.cleanup();\n * clearFocusableContainers();\n * }\n */\nexport const clearFocusableContainers = (uniqueId?: string) => {\n if (uniqueId) {\n delete containers[uniqueId];\n } else {\n Object.keys(containers).forEach((key) => delete containers[key]);\n }\n};\n\n/**\n * Traps keyboard focus within registered focusable containers.\n *\n * This function creates a focus trap that prevents Tab/Shift+Tab navigation from leaving\n * the registered containers. When the user reaches the last focusable element and presses Tab,\n * focus wraps to the first element, and vice versa with Shift+Tab.\n *\n * The function works with Shadow DOM by querying focusable elements from all registered\n * containers, which can include shadow roots from child components.\n *\n * @param wrapper - Optional wrapper element to attach the keydown listener to.\n * If not provided, defaults to document for backward compatibility.\n * Using a specific wrapper can improve performance and scope the listener.\n * @param uniqueId - Optional unique identifier to scope the focus trap to specific containers.\n * If provided, only containers registered with this uniqueId are included.\n * If not provided, uses the default key for backward compatibility.\n *\n * @returns An object with a `cleanup` method that removes event listeners.\n * Call this method when the component is closing or unmounting.\n *\n * @example\n * // In a parent component when opening\n * protected updated(changedProps) {\n * if (changedProps.has('open') && this.open) {\n * // Update signal so children can register\n * updateTearsheetSignals({ uniqueId: this.uniqueId });\n *\n * // Wait for children to register, then trap focus\n * requestAnimationFrame(() => {\n * this._trapFocusAPI = trapFocus(this as HTMLElement, this.uniqueId);\n * });\n * }\n * }\n *\n * @example\n * // Without uniqueId (uses default key)\n * this._trapFocusAPI = trapFocus();\n *\n * @example\n * // Cleanup when closing\n * disconnectedCallback() {\n * this._trapFocusAPI?.cleanup();\n * clearFocusableContainers(this.uniqueId);\n * }\n */\nexport const trapFocus = (wrapper?: HTMLElement, uniqueId?: string) => {\n const selectorTabbable = selectorTabbableCarbon.replace(\n `${carbonPrefix}-button`,\n `${carbonPrefix}-button:not([disabled]), ${carbonPrefix}-selectable-tag`\n );\n\n // Get containers for this uniqueId (or default key for backward compatibility)\n const id = uniqueId || DEFAULT_KEY;\n\n const containerArray = containers[id];\n\n if (!containerArray || containerArray.length === 0) {\n return { cleanup: () => {} };\n }\n\n // Use wrapper if provided, otherwise default to document for backward compatibility\n const eventTarget = wrapper || document;\n\n /**\n * Determines the best focusable element within a given container.\n *\n * @param el - The element to check for ability to focus\n * @returns The focusable element, or null if none found\n *\n * @internal\n */\n const getFocusTarget = (el) => {\n if (!el) {\n return null;\n }\n if (el.tagName === 'BUTTON' || el.tabIndex >= 0) {\n return el;\n }\n const inner = el.querySelector('button, [tabindex]:not([tabindex=\"-1\"])');\n return inner || el;\n };\n\n /**\n * Returns the actual focused element, resolving through Shadow DOM and slot boundaries.\n *\n * In standard DOM, `document.activeElement` returns the focused element. However, with\n * Shadow DOM and slots, this can be misleading:\n * - If focus is inside a shadow root, `document.activeElement` returns the host element,\n * not the actual focused element inside the shadow root.\n * - If focus is on a `<slot>` element, we need to resolve to the assigned element.\n *\n * This function traverses shadow roots to find the true focused element, stopping at\n * tabbable elements like buttons to avoid going too deep into component internals.\n *\n * @returns The actual focused element in the document\n *\n * @internal\n */\n const getRealActiveElement = () => {\n let activeElement = document.activeElement;\n\n // Traverse through nested shadow DOMs, but stop at tabbable elements\n // This ensures we stop at cds-button level, not go deeper into its shadow root\n while (\n activeElement &&\n activeElement.shadowRoot &&\n activeElement.shadowRoot.activeElement\n ) {\n const nextElement = activeElement.shadowRoot.activeElement;\n\n // Check if current element is a tabbable element (like cds-button)\n // If so, stop here instead of going deeper\n const tagName = activeElement.tagName.toLowerCase();\n if (\n selectorTabbable.indexOf(tagName) !== -1 ||\n tagName.includes('-button') ||\n activeElement.hasAttribute('tabindex')\n ) {\n break;\n }\n\n activeElement = nextElement;\n }\n\n // If active element is a slot, resolve to the assigned element\n if (activeElement?.tagName === 'SLOT') {\n let assigned: Element[] | undefined;\n\n if (activeElement instanceof HTMLSlotElement) {\n assigned = activeElement.assignedElements({ flatten: true });\n }\n const candidate = assigned?.find((node) =>\n node.matches('button, [tabindex]:not([tabindex=\"-1\"])')\n );\n if (candidate) {\n activeElement = candidate;\n }\n }\n\n return activeElement;\n };\n\n //Optionally focus first element immediately\n requestAnimationFrame(() => {\n const elements = getAllFocusableElements(containerArray);\n getFocusTarget(elements[0])?.focus({ preventScroll: true });\n });\n\n /**\n * Handles Tab and Shift+Tab key presses to trap focus within containers.\n *\n * @param e - The keyboard event\n *\n * @internal\n */\n function onKeyDown(e) {\n if (e.key !== 'Tab') {\n return;\n }\n\n // Flatten all focusable elements from all containers\n const elements = getAllFocusableElements(containerArray);\n\n const first = elements[0];\n const last = elements[elements.length - 1];\n const active = getRealActiveElement();\n\n if (e.shiftKey) {\n // Shift+Tab wrap\n if (active === first || first.contains(active)) {\n e.preventDefault();\n getFocusTarget(last)?.focus();\n }\n } else {\n // Tab wrap\n if (active === last || last.contains(active)) {\n e.preventDefault();\n getFocusTarget(first)?.focus();\n }\n }\n }\n\n /**\n * Retrieves all focusable elements from all registered containers.\n *\n * @param containers - Array of containers to query for focusable elements\n * @returns Array of all focusable elements found\n *\n * @internal\n */\n const getAllFocusableElements = (containers) =>\n containers\n .flatMap((container) => [...container.querySelectorAll(selectorTabbable)])\n .filter(Boolean);\n\n eventTarget.addEventListener('keydown', onKeyDown, true);\n\n return {\n cleanup: () => {\n eventTarget.removeEventListener('keydown', onKeyDown, true);\n // Don't clear containers here - let components unregister themselves\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import "../../globals/settings.js";
|
|
9
9
|
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
10
|
-
import { interstitialDetailsSignal, updateInterstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
11
10
|
import { registerFocusableContainers } from "../../utilities/manageFocusTrap/manageFocusTrap.js";
|
|
11
|
+
import { interstitialDetailsSignal, updateInterstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
12
12
|
import interstitial_screen_body_item_default$1 from "./interstitial-screen-body-item.scss.js";
|
|
13
13
|
import { LitElement, html } from "lit";
|
|
14
14
|
import { property } from "lit/decorators.js";
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import "../../globals/settings.js";
|
|
9
9
|
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
10
10
|
import "../action-set/index.js";
|
|
11
|
-
import { interstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
12
11
|
import { registerFocusableContainers } from "../../utilities/manageFocusTrap/manageFocusTrap.js";
|
|
12
|
+
import { interstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
13
13
|
import interstitial_screen_footer_default$1 from "./interstitial-screen-footer.scss.js";
|
|
14
14
|
import { html, nothing } from "lit";
|
|
15
15
|
import { property, state } from "lit/decorators.js";
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import "../../globals/settings.js";
|
|
9
9
|
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
10
|
-
import { interstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
11
10
|
import { registerFocusableContainers } from "../../utilities/manageFocusTrap/manageFocusTrap.js";
|
|
11
|
+
import { interstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
12
12
|
import interstitial_screen_header_default$1 from "./interstitial-screen-header.scss.js";
|
|
13
13
|
import { LitElement, html, nothing } from "lit";
|
|
14
14
|
import { property } from "lit/decorators.js";
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import "../../globals/settings.js";
|
|
9
9
|
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
10
|
+
import { clearFocusableContainers, trapFocus } from "../../utilities/manageFocusTrap/manageFocusTrap.js";
|
|
10
11
|
import interstitial_screen_default$1 from "./interstitial-screen.scss.js";
|
|
11
12
|
import { interstitialDetailsSignal, resetInterstitialDetailsSignal, updateInterstitialDetailsSignal } from "./interstitial-screen-context.js";
|
|
12
|
-
import { trapFocus } from "../../utilities/manageFocusTrap/manageFocusTrap.js";
|
|
13
13
|
import { LitElement, html, nothing } from "lit";
|
|
14
14
|
import { property, query, state } from "lit/decorators.js";
|
|
15
15
|
import HostListenerMixin from "@carbon/web-components/es/globals/mixins/host-listener.js";
|
|
@@ -84,6 +84,7 @@ let CDSInterstitialScreen = class CDSInterstitialScreen extends SignalWatcher(Ho
|
|
|
84
84
|
const { carouselAPI } = interstitialDetailsSignal.get();
|
|
85
85
|
carouselAPI?.destroyEvents?.();
|
|
86
86
|
this._trapFocusAPI?.cleanup();
|
|
87
|
+
clearFocusableContainers();
|
|
87
88
|
}
|
|
88
89
|
firstUpdated() {
|
|
89
90
|
this.requestUpdate();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interstitial-screen.js","names":["styles","customElement"],"sources":["../../../src/components/interstitial-screen/interstitial-screen.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { carbonPrefix, prefix } from '../../globals/settings';\nimport '@carbon/web-components/es-custom/components/modal/index.js';\nimport HostListenerMixin from '@carbon/web-components/es/globals/mixins/host-listener.js';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\n\nimport styles from './interstitial-screen.scss?lit';\nimport { SignalWatcher } from '@lit-labs/signals';\nimport {\n interstitialDetailsSignal,\n resetInterstitialDetailsSignal,\n updateInterstitialDetailsSignal,\n} from './interstitial-screen-context';\nimport HostListener from '@carbon/web-components/es/globals/decorators/host-listener';\nimport { trapFocus } from '../../utilities/manageFocusTrap/manageFocusTrap';\n\nexport const blockClass = `${prefix}--interstitial-screen`;\n\nexport type disableButtonConfigType = {\n skip?: boolean;\n back?: boolean;\n next?: boolean;\n start?: boolean;\n};\n/**\n * interstitial-screen main component\n * @element c4p-interstitial-screen\n * @fires c4p-interstitial-opened - The custom event triggered after loading the component.\n * Its event.detail will provide you with carousal api methods for step navigation and method to disable any action button\n * * @fires c4p-interstitial-beingclosed - The name of the custom event fired before interstitial is being closed upon a user gesture.\n * Cancellation of this event stops the user-initiated action of closing the interstitial.\n * @fires c4p-interstitial-closed - The name of the custom event fired after this tearsheet is closed upon a user gesture.\n\n */\n\n@customElement(`${prefix}-interstitial-screen`)\nclass CDSInterstitialScreen extends SignalWatcher(\n HostListenerMixin(LitElement)\n) {\n /**\n * Specifies whether the component is shown as a full-screen\n * experience, else it is shown as a modal by default.\n */\n\n @property({ type: Boolean, reflect: true, attribute: 'fullscreen' })\n isFullScreen: boolean = false;\n /**\n * Specifies whether the component is currently open.\n */\n @property({ type: Boolean, reflect: true })\n open: boolean = false;\n\n @state()\n stepDetails: Array<{ stepTitle: string; name?: string }> = [];\n /**\n * @ignore\n */\n @query('cds-custom-modal-body') modalBody!: HTMLElement;\n\n private _wasOpen = false;\n private _trapFocusAPI: { cleanup: () => void } | null = null;\n\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener(`${prefix}-request-close`, this._handleClose);\n }\n disconnectedCallback(): void {\n const { carouselAPI } = interstitialDetailsSignal.get();\n carouselAPI?.destroyEvents?.();\n this._trapFocusAPI?.cleanup();\n }\n firstUpdated() {\n this.requestUpdate(); // Ensure re-render\n resetInterstitialDetailsSignal();\n\n updateInterstitialDetailsSignal({\n name: 'isFullScreen',\n detail: this.isFullScreen,\n });\n }\n\n updated(changedProps: Map<string | number | symbol, unknown>) {\n if (changedProps.has('open')) {\n const wasOpen = this._wasOpen;\n const isOpen = this.open;\n\n // Update the signal with the open state\n updateInterstitialDetailsSignal({\n name: 'open',\n detail: isOpen,\n });\n\n if (!wasOpen && isOpen) {\n this.dispatchInItializeEvent();\n // `focusableContainers` holds the containers where we can query DOM elements.\n // Our strategy here is to let child/slotted components register their containers,\n // which are then passed to `trapFocus`. This allows the utility to query elements\n // directly without being blocked by shadow DOM boundaries.\n\n this._trapFocusAPI = trapFocus();\n }\n\n this._wasOpen = isOpen;\n }\n }\n\n private dispatchInItializeEvent = () => {\n setTimeout(() => {\n const { carouselAPI } = interstitialDetailsSignal.get();\n this.dispatchEvent(\n new CustomEvent(\n (\n this.constructor as typeof CDSInterstitialScreen\n ).eventOnInterstitialOpened,\n {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: {\n carouselAPI: carouselAPI\n ? {\n next: carouselAPI.next,\n prev: carouselAPI.prev,\n reset: carouselAPI.reset,\n gotToStep: carouselAPI.goToIndex,\n }\n : undefined,\n setDisableActionButtons: this.setDisableActionButtons,\n },\n }\n )\n );\n });\n };\n\n /**\n * Handles `click` event on this element.\n *\n * @param event The event.\n */\n @HostListener('click')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleOutsideClick = (event: MouseEvent) => {\n const modal = this.shadowRoot?.querySelector(`${carbonPrefix}-modal`);\n const modalContent = modal?.shadowRoot?.querySelector(\n `.${carbonPrefix}--modal-container`\n );\n const path = event.composedPath();\n if (modalContent && !path.includes(modalContent)) {\n this._handleClose(event);\n }\n };\n\n private setDisableActionButtons = (config: disableButtonConfigType) => {\n updateInterstitialDetailsSignal({ name: 'disableActions', detail: config });\n };\n\n _handleClose(e) {\n this.open = false;\n e.stopPropagation();\n\n const init = {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: {\n triggeredBy: e.detail.triggeredBy,\n },\n };\n if (\n this.dispatchEvent(\n new CustomEvent(\n (this.constructor as typeof CDSInterstitialScreen).eventBeforeClose,\n init\n )\n )\n ) {\n this.dispatchEvent(\n new CustomEvent(\n (this.constructor as typeof CDSInterstitialScreen).eventClose,\n init\n )\n );\n\n // Reset carousel and step after close event is dispatched\n const { carouselAPI } = interstitialDetailsSignal.get();\n if (carouselAPI) {\n carouselAPI.reset();\n }\n\n // Reset the current step to 0\n updateInterstitialDetailsSignal({\n name: 'currentStep',\n detail: 0,\n });\n }\n }\n\n //template methods\n\n renderFullScreen() {\n return html`\n <div class=\"${blockClass}--container\">\n <slot name=\"header\"></slot>\n <slot name=\"body\"></slot>\n <slot name=\"footer\"></slot>\n </div>\n `;\n }\n\n renderModal() {\n return html`<cds-custom-modal\n key=${this.open}\n ?prevent-close-on-click-outside=\"true\"\n class=${blockClass}\n size=\"lg\"\n ?open=\"${this.open}\"\n >\n <slot name=\"header\"></slot>\n <cds-custom-modal-body class=\"${blockClass}__body-container\">\n <slot name=\"body\"></slot>\n </cds-custom-modal-body>\n <cds-custom-modal-footer>\n <slot name=\"footer\"></slot>\n </cds-custom-modal-footer>\n </cds-custom-modal>`;\n }\n\n render() {\n return this.open\n ? this.isFullScreen\n ? html`${this.renderFullScreen()}`\n : html`${this.renderModal()}`\n : nothing;\n }\n\n static styles = styles;\n\n static get eventOnInterstitialOpened() {\n return `${prefix}-interstitial-opened`;\n }\n /**\n \n * The name of the custom event fired before interstitial is being closed upon a user gesture.\n * Cancellation of this event stops the user-initiated action of closing the interstitial.\n */\n static get eventBeforeClose() {\n return `${prefix}-interstitial-beingclosed`;\n }\n\n /**\n * The name of the custom event fired after this tearsheet is closed upon a user gesture.\n */\n static get eventClose() {\n return `${prefix}-interstitial-closed`;\n }\n}\n\nexport default CDSInterstitialScreen;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interstitial-screen.js","names":["styles","customElement"],"sources":["../../../src/components/interstitial-screen/interstitial-screen.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { carbonPrefix, prefix } from '../../globals/settings';\nimport '@carbon/web-components/es-custom/components/modal/index.js';\nimport HostListenerMixin from '@carbon/web-components/es/globals/mixins/host-listener.js';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\n\nimport styles from './interstitial-screen.scss?lit';\nimport { SignalWatcher } from '@lit-labs/signals';\nimport {\n interstitialDetailsSignal,\n resetInterstitialDetailsSignal,\n updateInterstitialDetailsSignal,\n} from './interstitial-screen-context';\nimport HostListener from '@carbon/web-components/es/globals/decorators/host-listener';\nimport {\n trapFocus,\n clearFocusableContainers,\n} from '../../utilities/manageFocusTrap/manageFocusTrap';\n\nexport const blockClass = `${prefix}--interstitial-screen`;\n\nexport type disableButtonConfigType = {\n skip?: boolean;\n back?: boolean;\n next?: boolean;\n start?: boolean;\n};\n/**\n * interstitial-screen main component\n * @element c4p-interstitial-screen\n * @fires c4p-interstitial-opened - The custom event triggered after loading the component.\n * Its event.detail will provide you with carousal api methods for step navigation and method to disable any action button\n * * @fires c4p-interstitial-beingclosed - The name of the custom event fired before interstitial is being closed upon a user gesture.\n * Cancellation of this event stops the user-initiated action of closing the interstitial.\n * @fires c4p-interstitial-closed - The name of the custom event fired after this tearsheet is closed upon a user gesture.\n\n */\n\n@customElement(`${prefix}-interstitial-screen`)\nclass CDSInterstitialScreen extends SignalWatcher(\n HostListenerMixin(LitElement)\n) {\n /**\n * Specifies whether the component is shown as a full-screen\n * experience, else it is shown as a modal by default.\n */\n\n @property({ type: Boolean, reflect: true, attribute: 'fullscreen' })\n isFullScreen: boolean = false;\n /**\n * Specifies whether the component is currently open.\n */\n @property({ type: Boolean, reflect: true })\n open: boolean = false;\n\n @state()\n stepDetails: Array<{ stepTitle: string; name?: string }> = [];\n /**\n * @ignore\n */\n @query('cds-custom-modal-body') modalBody!: HTMLElement;\n\n private _wasOpen = false;\n private _trapFocusAPI: { cleanup: () => void } | null = null;\n\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener(`${prefix}-request-close`, this._handleClose);\n }\n disconnectedCallback(): void {\n const { carouselAPI } = interstitialDetailsSignal.get();\n carouselAPI?.destroyEvents?.();\n this._trapFocusAPI?.cleanup();\n clearFocusableContainers();\n }\n firstUpdated() {\n this.requestUpdate(); // Ensure re-render\n resetInterstitialDetailsSignal();\n\n updateInterstitialDetailsSignal({\n name: 'isFullScreen',\n detail: this.isFullScreen,\n });\n }\n\n updated(changedProps: Map<string | number | symbol, unknown>) {\n if (changedProps.has('open')) {\n const wasOpen = this._wasOpen;\n const isOpen = this.open;\n\n // Update the signal with the open state\n updateInterstitialDetailsSignal({\n name: 'open',\n detail: isOpen,\n });\n\n if (!wasOpen && isOpen) {\n this.dispatchInItializeEvent();\n // `focusableContainers` holds the containers where we can query DOM elements.\n // Our strategy here is to let child/slotted components register their containers,\n // which are then passed to `trapFocus`. This allows the utility to query elements\n // directly without being blocked by shadow DOM boundaries.\n\n this._trapFocusAPI = trapFocus();\n }\n\n this._wasOpen = isOpen;\n }\n }\n\n private dispatchInItializeEvent = () => {\n setTimeout(() => {\n const { carouselAPI } = interstitialDetailsSignal.get();\n this.dispatchEvent(\n new CustomEvent(\n (\n this.constructor as typeof CDSInterstitialScreen\n ).eventOnInterstitialOpened,\n {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: {\n carouselAPI: carouselAPI\n ? {\n next: carouselAPI.next,\n prev: carouselAPI.prev,\n reset: carouselAPI.reset,\n gotToStep: carouselAPI.goToIndex,\n }\n : undefined,\n setDisableActionButtons: this.setDisableActionButtons,\n },\n }\n )\n );\n });\n };\n\n /**\n * Handles `click` event on this element.\n *\n * @param event The event.\n */\n @HostListener('click')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleOutsideClick = (event: MouseEvent) => {\n const modal = this.shadowRoot?.querySelector(`${carbonPrefix}-modal`);\n const modalContent = modal?.shadowRoot?.querySelector(\n `.${carbonPrefix}--modal-container`\n );\n const path = event.composedPath();\n if (modalContent && !path.includes(modalContent)) {\n this._handleClose(event);\n }\n };\n\n private setDisableActionButtons = (config: disableButtonConfigType) => {\n updateInterstitialDetailsSignal({ name: 'disableActions', detail: config });\n };\n\n _handleClose(e) {\n this.open = false;\n e.stopPropagation();\n\n const init = {\n bubbles: true,\n cancelable: true,\n composed: true,\n detail: {\n triggeredBy: e.detail.triggeredBy,\n },\n };\n if (\n this.dispatchEvent(\n new CustomEvent(\n (this.constructor as typeof CDSInterstitialScreen).eventBeforeClose,\n init\n )\n )\n ) {\n this.dispatchEvent(\n new CustomEvent(\n (this.constructor as typeof CDSInterstitialScreen).eventClose,\n init\n )\n );\n\n // Reset carousel and step after close event is dispatched\n const { carouselAPI } = interstitialDetailsSignal.get();\n if (carouselAPI) {\n carouselAPI.reset();\n }\n\n // Reset the current step to 0\n updateInterstitialDetailsSignal({\n name: 'currentStep',\n detail: 0,\n });\n }\n }\n\n //template methods\n\n renderFullScreen() {\n return html`\n <div class=\"${blockClass}--container\">\n <slot name=\"header\"></slot>\n <slot name=\"body\"></slot>\n <slot name=\"footer\"></slot>\n </div>\n `;\n }\n\n renderModal() {\n return html`<cds-custom-modal\n key=${this.open}\n ?prevent-close-on-click-outside=\"true\"\n class=${blockClass}\n size=\"lg\"\n ?open=\"${this.open}\"\n >\n <slot name=\"header\"></slot>\n <cds-custom-modal-body class=\"${blockClass}__body-container\">\n <slot name=\"body\"></slot>\n </cds-custom-modal-body>\n <cds-custom-modal-footer>\n <slot name=\"footer\"></slot>\n </cds-custom-modal-footer>\n </cds-custom-modal>`;\n }\n\n render() {\n return this.open\n ? this.isFullScreen\n ? html`${this.renderFullScreen()}`\n : html`${this.renderModal()}`\n : nothing;\n }\n\n static styles = styles;\n\n static get eventOnInterstitialOpened() {\n return `${prefix}-interstitial-opened`;\n }\n /**\n \n * The name of the custom event fired before interstitial is being closed upon a user gesture.\n * Cancellation of this event stops the user-initiated action of closing the interstitial.\n */\n static get eventBeforeClose() {\n return `${prefix}-interstitial-beingclosed`;\n }\n\n /**\n * The name of the custom event fired after this tearsheet is closed upon a user gesture.\n */\n static get eventClose() {\n return `${prefix}-interstitial-closed`;\n }\n}\n\nexport default CDSInterstitialScreen;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":[],"sources":["../../../src/components/page-header/context.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { createContext } from '@lit/context';\n\nexport const pageHeaderContext = createContext({\n headerOffset: 0,\n breadcrumbOffset: 0,\n fullyCollapsed: false,\n root: null,\n titleClipped: false,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAWA,MAAa,oBAAoB,cAAc;CAC7C,cAAc;CACd,kBAAkB;CAClB,gBAAgB;CAChB,MAAM;CACN,cAAc;
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../../../src/components/page-header/context.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { createContext } from '@lit/context';\n\nexport const pageHeaderContext = createContext({\n headerOffset: 0,\n breadcrumbOffset: 0,\n fullyCollapsed: false,\n root: null,\n titleClipped: false,\n disableStickyTabBar: false,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAWA,MAAa,oBAAoB,cAAc;CAC7C,cAAc;CACd,kBAAkB;CAClB,gBAAgB;CAChB,MAAM;CACN,cAAc;CACd,qBAAqB;CACtB,CAAC"}
|
|
@@ -11,7 +11,7 @@ import page_header_default from "./page-header.scss.js";
|
|
|
11
11
|
import { pageHeaderContext } from "./context.js";
|
|
12
12
|
import { classMap } from "lit/directives/class-map.js";
|
|
13
13
|
import { LitElement, html } from "lit";
|
|
14
|
-
import { property } from "lit/decorators.js";
|
|
14
|
+
import { property, state } from "lit/decorators.js";
|
|
15
15
|
import { carbonElement } from "@carbon/web-components/es/globals/decorators/carbon-element.js";
|
|
16
16
|
import { consume } from "@lit/context";
|
|
17
17
|
//#region src/components/page-header/page-header-breadcrumb.ts
|
|
@@ -35,6 +35,18 @@ let CDSPageHeaderBreadcrumb = class CDSPageHeaderBreadcrumb extends LitElement {
|
|
|
35
35
|
this.pageActionsFlush = false;
|
|
36
36
|
this.contentActionsFlush = false;
|
|
37
37
|
}
|
|
38
|
+
connectedCallback() {
|
|
39
|
+
super.connectedCallback();
|
|
40
|
+
this.updateFixedClass();
|
|
41
|
+
}
|
|
42
|
+
updated(changedProperties) {
|
|
43
|
+
super.updated(changedProperties);
|
|
44
|
+
if (changedProperties.has("context")) this.updateFixedClass();
|
|
45
|
+
}
|
|
46
|
+
updateFixedClass() {
|
|
47
|
+
if (this.context?.disableStickyTabBar) this.classList.add(`c4p--page-header-breadcrumb--fixed`);
|
|
48
|
+
else this.classList.remove(`c4p--page-header-breadcrumb--fixed`);
|
|
49
|
+
}
|
|
38
50
|
render() {
|
|
39
51
|
const { withinGrid, context } = this;
|
|
40
52
|
const { contentActionsClipped } = context ?? {};
|
|
@@ -69,7 +81,10 @@ let CDSPageHeaderBreadcrumb = class CDSPageHeaderBreadcrumb extends LitElement {
|
|
|
69
81
|
this.styles = page_header_default;
|
|
70
82
|
}
|
|
71
83
|
};
|
|
72
|
-
__decorate([property({
|
|
84
|
+
__decorate([property({
|
|
85
|
+
reflect: true,
|
|
86
|
+
type: Boolean
|
|
87
|
+
})], CDSPageHeaderBreadcrumb.prototype, "border", void 0);
|
|
73
88
|
__decorate([property({
|
|
74
89
|
attribute: "within-grid",
|
|
75
90
|
type: Boolean
|
|
@@ -85,7 +100,7 @@ __decorate([property({
|
|
|
85
100
|
__decorate([consume({
|
|
86
101
|
context: pageHeaderContext,
|
|
87
102
|
subscribe: true
|
|
88
|
-
})], CDSPageHeaderBreadcrumb.prototype, "context", void 0);
|
|
103
|
+
}), state()], CDSPageHeaderBreadcrumb.prototype, "context", void 0);
|
|
89
104
|
CDSPageHeaderBreadcrumb = __decorate([carbonElement(`c4p-page-header-breadcrumb`)], CDSPageHeaderBreadcrumb);
|
|
90
105
|
var page_header_breadcrumb_default = CDSPageHeaderBreadcrumb;
|
|
91
106
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-header-breadcrumb.js","names":["styles","customElement"],"sources":["../../../src/components/page-header/page-header-breadcrumb.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html } from 'lit';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { prefix, carbonPrefix } from '../../globals/settings';\nimport { property } from 'lit/decorators.js';\nimport styles from './page-header.scss?lit';\nimport { consume } from '@lit/context';\nimport { pageHeaderContext } from './context';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\n\n/**\n * Page header Breadcrumb Bar.\n * @element c4p-page-header-breadcrumb\n */\n@customElement(`${prefix}-page-header-breadcrumb`)\nclass CDSPageHeaderBreadcrumb extends LitElement {\n /**\n * Specify if breadcrumb bar has bottom border.\n */\n @property({ reflect: true })\n border = true;\n\n /**\n * Set to `true` if the breadcrumb bar is sitting within a grid\n * (ie. when used in tandem with page-header-hero-image)\n */\n @property({ attribute: 'within-grid', type: Boolean })\n withinGrid = false;\n\n /**\n * Set to `true` if page actions should be flush (no padding)\n */\n @property({ attribute: 'page-actions-flush', type: Boolean })\n pageActionsFlush = false;\n\n /**\n * Set to `true` if content actions should be flush (no padding)\n */\n @property({ attribute: 'content-actions-flush', type: Boolean })\n contentActionsFlush = false;\n\n @consume({ context: pageHeaderContext, subscribe: true })\n context;\n\n render() {\n const { withinGrid, context } = this;\n const { contentActionsClipped } = context ?? {};\n const gridClasses = classMap({\n [`${carbonPrefix}--css-grid`]: !withinGrid,\n [`${carbonPrefix}--subgrid ${carbonPrefix}--subgrid--wide`]: withinGrid,\n });\n\n const contentActionClasses = classMap({\n [`${prefix}--page-header__breadcrumb__content-actions-with-global-actions`]:\n true,\n [`${prefix}--page-header__breadcrumb__content-actions-with-global-actions--show`]:\n contentActionsClipped,\n });\n\n return html`\n <div class=\"${gridClasses}\">\n <div\n class=\"${carbonPrefix}--sm:col-span-4 ${carbonPrefix}--md:col-span-8 ${carbonPrefix}--lg:col-span-16 ${carbonPrefix}--css-grid-column\"\n >\n <div class=\"${prefix}--page-header__breadcrumb-container\">\n <div class=\"${prefix}--page-header__breadcrumb-wrapper\">\n <slot name=\"icon\"></slot>\n <slot></slot>\n </div>\n <div class=\"${prefix}--page-header__breadcrumb__actions\">\n <div class=\"${contentActionClasses}\">\n <slot name=\"content-actions\"></slot>\n </div>\n <slot name=\"page-actions\"></slot>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n static styles = styles;\n}\n\nexport default CDSPageHeaderBreadcrumb;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,IAAA,0BAAA,MACM,gCAAgC,WAAW;;;gBAKtC;oBAOI;0BAMM;6BAMG;;
|
|
1
|
+
{"version":3,"file":"page-header-breadcrumb.js","names":["styles","customElement"],"sources":["../../../src/components/page-header/page-header-breadcrumb.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html } from 'lit';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { prefix, carbonPrefix } from '../../globals/settings';\nimport { property, state } from 'lit/decorators.js';\nimport styles from './page-header.scss?lit';\nimport { consume } from '@lit/context';\nimport { pageHeaderContext } from './context';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\n\n/**\n * Page header Breadcrumb Bar.\n * @element c4p-page-header-breadcrumb\n */\n@customElement(`${prefix}-page-header-breadcrumb`)\nclass CDSPageHeaderBreadcrumb extends LitElement {\n /**\n * Specify if breadcrumb bar has bottom border.\n */\n @property({ reflect: true, type: Boolean })\n border = true;\n\n /**\n * Set to `true` if the breadcrumb bar is sitting within a grid\n * (ie. when used in tandem with page-header-hero-image)\n */\n @property({ attribute: 'within-grid', type: Boolean })\n withinGrid = false;\n\n /**\n * Set to `true` if page actions should be flush (no padding)\n */\n @property({ attribute: 'page-actions-flush', type: Boolean })\n pageActionsFlush = false;\n\n /**\n * Set to `true` if content actions should be flush (no padding)\n */\n @property({ attribute: 'content-actions-flush', type: Boolean })\n contentActionsFlush = false;\n\n @consume({ context: pageHeaderContext, subscribe: true })\n @state()\n context;\n\n connectedCallback() {\n super.connectedCallback();\n // Apply class on initial connection\n this.updateFixedClass();\n }\n\n updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('context')) {\n this.updateFixedClass();\n }\n }\n\n private updateFixedClass() {\n if (this.context?.disableStickyTabBar) {\n this.classList.add(`${prefix}--page-header-breadcrumb--fixed`);\n } else {\n this.classList.remove(`${prefix}--page-header-breadcrumb--fixed`);\n }\n }\n\n render() {\n const { withinGrid, context } = this;\n const { contentActionsClipped } = context ?? {};\n const gridClasses = classMap({\n [`${carbonPrefix}--css-grid`]: !withinGrid,\n [`${carbonPrefix}--subgrid ${carbonPrefix}--subgrid--wide`]: withinGrid,\n });\n\n const contentActionClasses = classMap({\n [`${prefix}--page-header__breadcrumb__content-actions-with-global-actions`]:\n true,\n [`${prefix}--page-header__breadcrumb__content-actions-with-global-actions--show`]:\n contentActionsClipped,\n });\n\n return html`\n <div class=\"${gridClasses}\">\n <div\n class=\"${carbonPrefix}--sm:col-span-4 ${carbonPrefix}--md:col-span-8 ${carbonPrefix}--lg:col-span-16 ${carbonPrefix}--css-grid-column\"\n >\n <div class=\"${prefix}--page-header__breadcrumb-container\">\n <div class=\"${prefix}--page-header__breadcrumb-wrapper\">\n <slot name=\"icon\"></slot>\n <slot></slot>\n </div>\n <div class=\"${prefix}--page-header__breadcrumb__actions\">\n <div class=\"${contentActionClasses}\">\n <slot name=\"content-actions\"></slot>\n </div>\n <slot name=\"page-actions\"></slot>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n static styles = styles;\n}\n\nexport default CDSPageHeaderBreadcrumb;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,IAAA,0BAAA,MACM,gCAAgC,WAAW;;;gBAKtC;oBAOI;0BAMM;6BAMG;;CAMtB,oBAAoB;AAClB,QAAM,mBAAmB;AAEzB,OAAK,kBAAkB;;CAGzB,QAAQ,mBAAqC;AAC3C,QAAM,QAAQ,kBAAkB;AAChC,MAAI,kBAAkB,IAAI,UAAU,CAClC,MAAK,kBAAkB;;CAI3B,mBAA2B;AACzB,MAAI,KAAK,SAAS,oBAChB,MAAK,UAAU,IAAI,qCAA2C;MAE9D,MAAK,UAAU,OAAO,qCAA2C;;CAIrE,SAAS;EACP,MAAM,EAAE,YAAY,YAAY;EAChC,MAAM,EAAE,0BAA0B,WAAW,EAAE;AAa/C,SAAO,IAAI;oBAZS,SAAS;IAC1B,kBAA8B,CAAC;IAC/B,oCAA4D;GAC9D,CAU0B,CAAC;;yBAEA,kBAAA,MAA+B,kBAAA,MAA+B,mBAAA,MAAgC;;8BAE/F;gCACE;;;;gCAIA;4BAjBA,SAAS;IACnC,sEACC;IACD,4EACC;GACH,CAa2C,CAAC;;;;;;;;;;;gBAW/BA;;;YApFf,SAAS;CAAE,SAAS;CAAM,MAAM;CAAS,CAAC,CAAA,EAAA,wBAAA,WAAA,UAAA,KAAA,EAAA;YAO1C,SAAS;CAAE,WAAW;CAAe,MAAM;CAAS,CAAC,CAAA,EAAA,wBAAA,WAAA,cAAA,KAAA,EAAA;YAMrD,SAAS;CAAE,WAAW;CAAsB,MAAM;CAAS,CAAC,CAAA,EAAA,wBAAA,WAAA,oBAAA,KAAA,EAAA;YAM5D,SAAS;CAAE,WAAW;CAAyB,MAAM;CAAS,CAAC,CAAA,EAAA,wBAAA,WAAA,uBAAA,KAAA,EAAA;YAG/D,QAAQ;CAAE,SAAS;CAAmB,WAAW;CAAM,CAAC,EACxD,OAAO,CAAA,EAAA,wBAAA,WAAA,WAAA,KAAA,EAAA;sCA5BTC,cAAc,6BAAmC,CAAA,EAAA,wBAAA;AA4FlD,IAAA,iCAAe"}
|
|
@@ -9,6 +9,7 @@ import "../../globals/settings.js";
|
|
|
9
9
|
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
|
|
10
10
|
import page_header_default from "./page-header.scss.js";
|
|
11
11
|
import { LitElement, html } from "lit";
|
|
12
|
+
import { property } from "lit/decorators.js";
|
|
12
13
|
import { carbonElement } from "@carbon/web-components/es/globals/decorators/carbon-element.js";
|
|
13
14
|
//#region src/components/page-header/page-header-tabs.ts
|
|
14
15
|
/**
|
|
@@ -24,6 +25,28 @@ import { carbonElement } from "@carbon/web-components/es/globals/decorators/carb
|
|
|
24
25
|
* @element c4p-page-header-tabs
|
|
25
26
|
*/
|
|
26
27
|
let CDSPageHeaderTabs = class CDSPageHeaderTabs extends LitElement {
|
|
28
|
+
constructor(..._args) {
|
|
29
|
+
super(..._args);
|
|
30
|
+
this.disableStickyTabBar = false;
|
|
31
|
+
}
|
|
32
|
+
connectedCallback() {
|
|
33
|
+
super.connectedCallback();
|
|
34
|
+
this.updateContext();
|
|
35
|
+
}
|
|
36
|
+
updated(changedProperties) {
|
|
37
|
+
super.updated(changedProperties);
|
|
38
|
+
if (changedProperties.has("disableStickyTabBar")) this.updateContext();
|
|
39
|
+
}
|
|
40
|
+
updateContext() {
|
|
41
|
+
const pageHeader = this.closest(`c4p-page-header`);
|
|
42
|
+
if (pageHeader) {
|
|
43
|
+
pageHeader.context = {
|
|
44
|
+
...pageHeader.context,
|
|
45
|
+
disableStickyTabBar: this.disableStickyTabBar
|
|
46
|
+
};
|
|
47
|
+
pageHeader.requestUpdate("context");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
27
50
|
render() {
|
|
28
51
|
return html` <div class="${"cds-custom"}--css-grid" condensed="">
|
|
29
52
|
<div
|
|
@@ -41,6 +64,10 @@ let CDSPageHeaderTabs = class CDSPageHeaderTabs extends LitElement {
|
|
|
41
64
|
this.styles = page_header_default;
|
|
42
65
|
}
|
|
43
66
|
};
|
|
67
|
+
__decorate([property({
|
|
68
|
+
type: Boolean,
|
|
69
|
+
attribute: "disable-sticky-tab-bar"
|
|
70
|
+
})], CDSPageHeaderTabs.prototype, "disableStickyTabBar", void 0);
|
|
44
71
|
CDSPageHeaderTabs = __decorate([carbonElement(`c4p-page-header-tabs`)], CDSPageHeaderTabs);
|
|
45
72
|
var page_header_tabs_default = CDSPageHeaderTabs;
|
|
46
73
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-header-tabs.js","names":["styles","customElement"],"sources":["../../../src/components/page-header/page-header-tabs.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html } from 'lit';\nimport { prefix, carbonPrefix } from '../../globals/settings';\nimport styles from './page-header.scss?lit';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\n\n/**\n * Page header Tabs Bar.\n * @element c4p-page-header-tabs\n */\n@customElement(`${prefix}-page-header-tabs`)\nclass CDSPageHeaderTabs extends LitElement {\n render() {\n return html` <div class=\"${carbonPrefix}--css-grid\" condensed=\"\">\n <div\n class=\"${carbonPrefix}--sm:col-span-4 ${carbonPrefix}--md:col-span-8 ${carbonPrefix}--lg:col-span-16 ${carbonPrefix}--css-grid-column\"\n >\n <div class=\"${prefix}--page-header__tab-bar--tablist\">\n <slot></slot>\n <slot name=\"tags\"></slot>\n </div>\n </div>\n <slot name=\"scroller\"></slot>\n </div>`;\n }\n\n static styles = styles;\n}\n\nexport default CDSPageHeaderTabs;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"page-header-tabs.js","names":["styles","customElement"],"sources":["../../../src/components/page-header/page-header-tabs.ts"],"sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2025, 2025\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement, html } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { prefix, carbonPrefix } from '../../globals/settings';\nimport styles from './page-header.scss?lit';\nimport { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';\nimport CDSPageHeader from './page-header';\n\n/**\n * Page header Tabs Bar.\n * @element c4p-page-header-tabs\n */\n@customElement(`${prefix}-page-header-tabs`)\nclass CDSPageHeaderTabs extends LitElement {\n /**\n * Disable sticky positioning for the tab bar\n */\n @property({ type: Boolean, attribute: 'disable-sticky-tab-bar' })\n disableStickyTabBar = false;\n\n connectedCallback() {\n super.connectedCallback();\n this.updateContext();\n }\n\n updated(changedProperties: Map<string, any>) {\n super.updated(changedProperties);\n if (changedProperties.has('disableStickyTabBar')) {\n this.updateContext();\n }\n }\n\n private updateContext() {\n const pageHeader = this.closest(`${prefix}-page-header`) as CDSPageHeader;\n if (pageHeader) {\n // Create a new object to trigger reactivity\n pageHeader.context = {\n ...pageHeader.context,\n disableStickyTabBar: this.disableStickyTabBar,\n };\n // Force update\n pageHeader.requestUpdate('context');\n }\n }\n\n render() {\n return html` <div class=\"${carbonPrefix}--css-grid\" condensed=\"\">\n <div\n class=\"${carbonPrefix}--sm:col-span-4 ${carbonPrefix}--md:col-span-8 ${carbonPrefix}--lg:col-span-16 ${carbonPrefix}--css-grid-column\"\n >\n <div class=\"${prefix}--page-header__tab-bar--tablist\">\n <slot></slot>\n <slot name=\"tags\"></slot>\n </div>\n </div>\n <slot name=\"scroller\"></slot>\n </div>`;\n }\n\n static styles = styles;\n}\n\nexport default CDSPageHeaderTabs;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,IAAA,oBAAA,MACM,0BAA0B,WAAW;;;6BAKnB;;CAEtB,oBAAoB;AAClB,QAAM,mBAAmB;AACzB,OAAK,eAAe;;CAGtB,QAAQ,mBAAqC;AAC3C,QAAM,QAAQ,kBAAkB;AAChC,MAAI,kBAAkB,IAAI,sBAAsB,CAC9C,MAAK,eAAe;;CAIxB,gBAAwB;EACtB,MAAM,aAAa,KAAK,QAAQ,kBAAwB;AACxD,MAAI,YAAY;AAEd,cAAW,UAAU;IACnB,GAAG,WAAW;IACd,qBAAqB,KAAK;IAC3B;AAED,cAAW,cAAc,UAAU;;;CAIvC,SAAS;AACP,SAAO,IAAI,gBAAA,MAA6B;;uBAEd,kBAAA,MAA+B,kBAAA,MAA+B,mBAAA,MAAgC;;4BAE/F;;;;;;;;;gBASXA;;;YA1Cf,SAAS;CAAE,MAAM;CAAS,WAAW;CAA0B,CAAC,CAAA,EAAA,kBAAA,WAAA,uBAAA,KAAA,EAAA;gCALlEC,cAAc,uBAA6B,CAAA,EAAA,kBAAA;AAkD5C,IAAA,2BAAe"}
|
|
@@ -33,6 +33,11 @@ let CDSPageHeader = class CDSPageHeader extends LitElement {
|
|
|
33
33
|
super(..._args);
|
|
34
34
|
this.context = {};
|
|
35
35
|
}
|
|
36
|
+
updated(changedProperties) {
|
|
37
|
+
super.updated(changedProperties);
|
|
38
|
+
if (changedProperties.has("context")) if (this.context.disableStickyTabBar) this.classList.add(`c4p--page-header--disable-sticky-tab-bar`);
|
|
39
|
+
else this.classList.remove(`c4p--page-header--disable-sticky-tab-bar`);
|
|
40
|
+
}
|
|
36
41
|
connectedCallback() {
|
|
37
42
|
super.connectedCallback();
|
|
38
43
|
const contentElement = this.querySelector(`c4p-page-header-content`);
|