@qld-gov-au/qgds-bootstrap5 2.0.9 → 2.0.11
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/.storybook/main.mjs +2 -2
- package/dist/assets/components/bs5/banner/banner.hbs +3 -6
- package/dist/assets/components/bs5/card/card.hbs +2 -2
- package/dist/assets/components/bs5/head/head.hbs +1 -1
- package/dist/assets/components/bs5/tag/tag.hbs +1 -1
- package/dist/assets/css/qld.bootstrap.css +2 -1
- package/dist/assets/css/qld.bootstrap.css.map +2 -2
- package/dist/assets/css/qld.bootstrap.legacy.css +2 -1
- package/dist/assets/css/qld.bootstrap.legacy.css.map +2 -2
- package/dist/assets/js/handlebars.helpers.bundle.js +2 -1
- package/dist/assets/js/handlebars.helpers.bundle.js.map +2 -2
- package/dist/assets/js/handlebars.init.min.js +9 -11
- package/dist/assets/js/handlebars.init.min.js.map +2 -2
- package/dist/assets/js/handlebars.partials.js +9 -11
- package/dist/assets/js/handlebars.partials.js.map +2 -2
- package/dist/assets/js/qld.bootstrap.min.js +6 -5
- package/dist/assets/js/qld.bootstrap.min.js.map +4 -4
- package/dist/assets/node/handlebars.init.min.js +9 -5
- package/dist/assets/node/handlebars.init.min.js.map +2 -2
- package/dist/components/bs5/banner/banner.hbs +3 -6
- package/dist/components/bs5/card/card.hbs +2 -2
- package/dist/components/bs5/head/head.hbs +1 -1
- package/dist/components/bs5/tag/tag.hbs +1 -1
- package/dist/package.json +1 -1
- package/dist/sample-data/card/card.data.json +4 -1
- package/dist/sample-data/tag/tag.data.json +149 -143
- package/esbuild.js +8 -0
- package/package.json +1 -1
- package/src/components/bs5/banner/banner.hbs +3 -6
- package/src/components/bs5/banner/banner.scss +10 -7
- package/src/components/bs5/banner/banner.stories.js +2 -5
- package/src/components/bs5/button/button.scss +4 -11
- package/src/components/bs5/button/button.stories.js +17 -15
- package/src/components/bs5/card/Card.js +31 -2
- package/src/components/bs5/card/Card.mdx +4 -0
- package/src/components/bs5/card/card--icon-list-footer.stories.js +2 -24
- package/src/components/bs5/card/card--multi-action.stories.js +9 -28
- package/src/components/bs5/card/card--no-action.stories.js +5 -27
- package/src/components/bs5/card/card--single-action.stories.js +4 -33
- package/src/components/bs5/card/card.data.json +4 -1
- package/src/components/bs5/card/card.hbs +2 -2
- package/src/components/bs5/footer/footer_formio.scss +5 -5
- package/src/components/bs5/modal/modal.scss +106 -99
- package/src/components/bs5/navbar/navbar.functions.js +122 -19
- package/src/components/bs5/tag/tag--status.stories.js +1 -0
- package/src/components/bs5/tag/tag.data.json +149 -143
- package/src/components/bs5/tag/tag.hbs +1 -1
- package/src/components/bs5/tag/tag.scss +2 -5
- package/src/components/bs5/tag/tag.stories.js +1 -0
- package/src/components/bs5/typography/typography.stories.js +1 -1
- package/src/css/mixins/focusable.scss +1 -1
- package/src/css/mixins/make-icon.scss +1 -1
- package/src/js/handlebars.helpers.js +9 -1
- package/src/js/utils.js +142 -0
package/src/js/utils.js
CHANGED
|
@@ -47,3 +47,145 @@ export function isFocusable(element) {
|
|
|
47
47
|
}
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all focusable elements within a container
|
|
53
|
+
* @param {HTMLElement} container The container element to search within
|
|
54
|
+
* @returns {HTMLElement[]} Array of focusable elements
|
|
55
|
+
*/
|
|
56
|
+
export function getFocusableElements(container) {
|
|
57
|
+
if (!container) return [];
|
|
58
|
+
|
|
59
|
+
const allElements = container.querySelectorAll("*");
|
|
60
|
+
return Array.from(allElements).filter((el) => isFocusable(el));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a focus trap for accessibility
|
|
65
|
+
* Traps keyboard focus within a container element (e.g., modal, navbar, menu)
|
|
66
|
+
* @param {HTMLElement} container The container element to trap focus within
|
|
67
|
+
* @param {Object} options Configuration options
|
|
68
|
+
* @param {HTMLElement} options.returnFocusElement Element to return focus to when deactivated
|
|
69
|
+
* @param {Function} options.onEscape Callback function when Escape key is pressed
|
|
70
|
+
* @returns {Object} Focus trap controller with activate, deactivate, and update methods
|
|
71
|
+
*/
|
|
72
|
+
export function createFocusTrap(container, options = {}) {
|
|
73
|
+
if (!container) {
|
|
74
|
+
throw new Error("Container element is required for focus trap");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { returnFocusElement, onEscape } = options;
|
|
78
|
+
let isActive = false;
|
|
79
|
+
let focusableElements = [];
|
|
80
|
+
let previousActiveElement = null;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Update the list of focusable elements
|
|
84
|
+
*/
|
|
85
|
+
function updateFocusableElements() {
|
|
86
|
+
focusableElements = getFocusableElements(container);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Handle Tab key navigation within the trap
|
|
91
|
+
*/
|
|
92
|
+
function handleTabKey(event) {
|
|
93
|
+
if (!isActive || focusableElements.length === 0) return;
|
|
94
|
+
|
|
95
|
+
const firstElement = focusableElements[0];
|
|
96
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
97
|
+
const activeElement = document.activeElement;
|
|
98
|
+
|
|
99
|
+
// Shift + Tab (backward)
|
|
100
|
+
if (event.shiftKey) {
|
|
101
|
+
if (
|
|
102
|
+
activeElement === firstElement ||
|
|
103
|
+
!container.contains(activeElement)
|
|
104
|
+
) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
lastElement.focus();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Tab (forward)
|
|
110
|
+
else {
|
|
111
|
+
if (activeElement === lastElement || !container.contains(activeElement)) {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
firstElement.focus();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handle Escape key to close/deactivate
|
|
120
|
+
*/
|
|
121
|
+
function handleEscapeKey(event) {
|
|
122
|
+
if (!isActive) return;
|
|
123
|
+
|
|
124
|
+
if (event.key === "Escape" || event.key === "Esc") {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
if (typeof onEscape === "function") {
|
|
127
|
+
onEscape();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle all keyboard events
|
|
134
|
+
*/
|
|
135
|
+
function handleKeyDown(event) {
|
|
136
|
+
if (event.key === "Tab") {
|
|
137
|
+
handleTabKey(event);
|
|
138
|
+
} else if (event.key === "Escape" || event.key === "Esc") {
|
|
139
|
+
handleEscapeKey(event);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Activate the focus trap
|
|
145
|
+
*/
|
|
146
|
+
function activate() {
|
|
147
|
+
if (isActive) return;
|
|
148
|
+
|
|
149
|
+
// Store the currently focused element to return focus later
|
|
150
|
+
previousActiveElement = document.activeElement;
|
|
151
|
+
|
|
152
|
+
// Update focusable elements
|
|
153
|
+
updateFocusableElements();
|
|
154
|
+
|
|
155
|
+
// Add keyboard event listener
|
|
156
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
157
|
+
|
|
158
|
+
// Focus the first focusable element
|
|
159
|
+
if (focusableElements.length > 0) {
|
|
160
|
+
focusableElements[0].focus();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
isActive = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Deactivate the focus trap
|
|
168
|
+
*/
|
|
169
|
+
function deactivate() {
|
|
170
|
+
if (!isActive) return;
|
|
171
|
+
|
|
172
|
+
// Remove keyboard event listener
|
|
173
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
174
|
+
|
|
175
|
+
// Return focus to the element that had focus before activation
|
|
176
|
+
const elementToFocus = returnFocusElement || previousActiveElement;
|
|
177
|
+
if (elementToFocus && typeof elementToFocus.focus === "function") {
|
|
178
|
+
elementToFocus.focus();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
isActive = false;
|
|
182
|
+
previousActiveElement = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
activate,
|
|
187
|
+
deactivate,
|
|
188
|
+
update: updateFocusableElements,
|
|
189
|
+
isActive: () => isActive,
|
|
190
|
+
};
|
|
191
|
+
}
|