@brightspot/ui 6.7.0 → 6.7.1-pr.405.2026.10.26
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/dist/storybook/project.json +1 -1
- package/package.json +1 -1
- package/src/legacy/tool-ui/src/ListManager.css +22 -1
- package/src/legacy/tool-ui/src/SkipLinks.css +3 -3
- package/src/legacy/tool-ui/src/SkipLinks.ts +93 -84
- package/src/legacy/tool-ui/src/main/webapp/dist/{RTEProseMirror.54b4c735cf930827fa7c.js → RTEProseMirror.e4f881c67b2f3865776b.js} +6 -6
- package/src/legacy/tool-ui/src/main/webapp/dist/{v4.b1cc13348e098860f3f2.js → v4.94878a2d8b5c7787df69.js} +2 -2
- package/src/legacy/tool-ui/src/main/webapp/dist/{v5.c39ee07e5ec4ce2be423.css → v5.367effd3850ba2b3ccb2.css} +2 -2
- package/src/legacy/tool-ui/src/main/webapp/dist/{v5.a627e1a0e699d6dc16d2.js → v5.de9169aefaf9fc4323ff.js} +3 -3
- /package/src/legacy/tool-ui/src/main/webapp/dist/{RTEProseMirror.54b4c735cf930827fa7c.js.LICENSE.txt → RTEProseMirror.e4f881c67b2f3865776b.js.LICENSE.txt} +0 -0
- /package/src/legacy/tool-ui/src/main/webapp/dist/{v4.b1cc13348e098860f3f2.js.LICENSE.txt → v4.94878a2d8b5c7787df69.js.LICENSE.txt} +0 -0
- /package/src/legacy/tool-ui/src/main/webapp/dist/{v5.a627e1a0e699d6dc16d2.js.LICENSE.txt → v5.de9169aefaf9fc4323ff.js.LICENSE.txt} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"generatedAt":
|
|
1
|
+
{"generatedAt":1793014295499,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":true,"hasStorybookEslint":true,"refCount":0,"knownPackages":{"testPackages":{"@storybook/addon-vitest":"10.2.12","@vitest/browser-playwright":"4.1.11","playwright":"1.58.2","vitest":"4.1.11"},"stylingPackages":{"tailwindcss":"^3.4.13","autoprefixer":"10.4.21"}},"hasRouterPackage":false,"packageManager":{"type":"yarn","agent":"yarn","nodeLinker":"undefined"},"preview":{"usesGlobals":true},"framework":{"name":"@storybook/web-components-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/web-components","portableStoriesFileCount":0,"applicationFileCount":6,"storybookVersion":"10.2.12","storybookVersionSpecifier":"10.2.12","language":"typescript","storybookPackages":{"@storybook/addon-themes":{"version":"10.2.12"},"@storybook/web-components-vite":{"version":"10.2.12"},"@wc-toolkit/storybook-helpers":{"version":"10.0.0"},"eslint-plugin-storybook":{"version":"10.0.7"},"storybook":{}},"addons":{"@storybook/addon-docs":{"version":"10.2.12"},"@storybook/addon-vitest":{"version":"10.2.12"}},"packageJsonType":"module"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
.ListManager-widget {
|
|
6
|
-
@apply absolute -top-10
|
|
6
|
+
@apply absolute -top-10 start-16 z-10 flex flex-col overflow-clip rounded-2xl border border-gray-100 bg-white shadow-lg;
|
|
7
7
|
|
|
8
8
|
.Widget-title {
|
|
9
9
|
@apply static !pe-[60px] text-sm;
|
|
@@ -25,3 +25,24 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/* Anchor the menu to the list's first item where supported; browsers without
|
|
30
|
+
anchor positioning drop these declarations and keep the base -top-10/start-16
|
|
31
|
+
placement. anchor-scope keeps the name private to each <ol> so it resolves to
|
|
32
|
+
that list's own first item. The menu opens as a direct child of the <ol> (see
|
|
33
|
+
ListManager.js), making it a sibling of this anchor. inset-inline-start stays
|
|
34
|
+
logical so the RTL build doesn't flip it to a physical right edge. */
|
|
35
|
+
.ProseMirror ol {
|
|
36
|
+
anchor-scope: --list-first-item;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ProseMirror ol > li:first-child {
|
|
40
|
+
anchor-name: --list-first-item;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ListManager-widget {
|
|
44
|
+
position-anchor: --list-first-item;
|
|
45
|
+
top: anchor(top);
|
|
46
|
+
inset-inline-start: anchor(start);
|
|
47
|
+
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
|
|
48
|
+
}
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
.SkipLinks-heading {
|
|
10
|
-
@apply btu-heading-5 px-4 py-2 text-gray-900;
|
|
10
|
+
@apply btu-heading-5 flex w-full px-4 py-2 text-gray-900;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.SkipLinks-note {
|
|
14
|
-
@apply border-t border-t-gray-200 px-4 pb-2 pt-4 text-sm text-gray-500;
|
|
14
|
+
@apply flex w-full border-t border-t-gray-200 px-4 pb-2 pt-4 text-sm text-gray-500;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.SkipLinks-list {
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
.SkipLinks-link {
|
|
22
|
-
@apply btu-button btu-button-fill-none btu-button-primary w-full justify-start bg-white;
|
|
22
|
+
@apply btu-button btu-button-fill-none btu-button-primary w-full justify-start bg-white no-underline;
|
|
23
23
|
}
|
|
@@ -1,112 +1,121 @@
|
|
|
1
1
|
import './SkipLinks.css'
|
|
2
2
|
import onFind from './dom/onFind'
|
|
3
|
-
import { focusRegionManager } from './FocusRegions'
|
|
3
|
+
import { focusRegionManager, type FocusRegionConfig } from './FocusRegions'
|
|
4
4
|
import create from './dom/create'
|
|
5
|
-
import { localizeShortcut } from './platform'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let list: HTMLElement | null = null
|
|
10
|
-
let note: HTMLElement | null = null
|
|
11
|
-
let lastRegionKey = ''
|
|
6
|
+
// The nav (static landmark links) is server-rendered; we enhance activation via the focus
|
|
7
|
+
// region manager and append the other currently-visible regions the server can't know about.
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
// Rendered statically by the backend — skipped when appending dynamic regions.
|
|
10
|
+
const STATIC_REGION_IDS = new Set(['page-header', 'page-nav', 'page-main'])
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
onFind('.SkipLinks', (nav: HTMLElement) => {
|
|
13
|
+
const list = nav.querySelector<HTMLElement>('.SkipLinks-list')
|
|
14
|
+
if (!list) return
|
|
15
|
+
|
|
16
|
+
const skipToPrefix =
|
|
17
|
+
nav.querySelector('.SkipLinks-heading')?.textContent?.trim() ?? ''
|
|
18
|
+
|
|
19
|
+
// Signature of the rendered dynamic links, so we only rebuild — and risk disturbing
|
|
20
|
+
// focus — when the visible region set actually changes.
|
|
21
|
+
let lastKey = ''
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
const
|
|
23
|
+
const refreshDynamicLinks = (): void => {
|
|
24
|
+
const layer = focusRegionManager.getTopLayer()
|
|
25
|
+
const regions = focusRegionManager
|
|
26
|
+
.getVisibleRegions(layer)
|
|
27
|
+
.filter((config) => !STATIC_REGION_IDS.has(config.id))
|
|
21
28
|
.map((config) => ({
|
|
22
29
|
config,
|
|
23
30
|
label: focusRegionManager.getRegionLabel(config),
|
|
24
31
|
}))
|
|
25
32
|
.filter(
|
|
26
|
-
(
|
|
27
|
-
|
|
33
|
+
(region): region is { config: FocusRegionConfig; label: string } =>
|
|
34
|
+
region.label !== null,
|
|
28
35
|
)
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
const key = regions.map((r) => `${r.config.id}:${r.label}`).join('|')
|
|
38
|
+
if (key === lastKey) return
|
|
39
|
+
lastKey = key
|
|
40
|
+
|
|
41
|
+
const active = document.activeElement
|
|
42
|
+
const rebuildingFocusedLink =
|
|
43
|
+
active instanceof HTMLElement &&
|
|
44
|
+
list.contains(active) &&
|
|
45
|
+
active.closest('[data-skip-dynamic]') !== null
|
|
46
|
+
|
|
47
|
+
list
|
|
48
|
+
.querySelectorAll('[data-skip-dynamic]')
|
|
49
|
+
.forEach((item) => item.remove())
|
|
50
|
+
|
|
51
|
+
for (const { config, label } of regions) {
|
|
52
|
+
// Match the static links: an <a href="#id">. A hrefless <a> (or a <button>, which
|
|
53
|
+
// Safari omits from Tab order) wouldn't be reliably tab-reachable. Give the region an
|
|
54
|
+
// id to point at when it lacks one.
|
|
55
|
+
if (!config.element.id) {
|
|
56
|
+
config.element.id = `skipRegion-${config.id}`
|
|
57
|
+
}
|
|
50
58
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
const link = create('a', {
|
|
60
|
+
className: 'SkipLinks-link',
|
|
61
|
+
href: `#${config.element.id}`,
|
|
62
|
+
'data-skip-region': config.id,
|
|
63
|
+
'aria-label': `${skipToPrefix} ${label}`,
|
|
64
|
+
textContent: label,
|
|
56
65
|
})
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
const item = create('li', { 'data-skip-dynamic': '' })
|
|
68
|
+
item.append(link)
|
|
69
|
+
list.append(item)
|
|
70
|
+
}
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
// The rebuild removed the link that had focus; keep focus in the menu.
|
|
73
|
+
if (rebuildingFocusedLink) {
|
|
74
|
+
list.querySelector<HTMLElement>('.SkipLinks-link')?.focus()
|
|
64
75
|
}
|
|
76
|
+
}
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for (const { config, label } of labeledRegions) {
|
|
72
|
-
const li = create('li')
|
|
73
|
-
const link = create('button', {
|
|
74
|
-
className: 'SkipLinks-link',
|
|
75
|
-
type: 'button',
|
|
76
|
-
textContent: label,
|
|
77
|
-
'aria-label': `${skipToPrefix} ${label}`,
|
|
78
|
-
})
|
|
79
|
-
link.addEventListener('click', () => {
|
|
80
|
-
if (!config.element.isConnected) return
|
|
81
|
-
focusRegionManager.focusRegion(config, document.body)
|
|
82
|
-
})
|
|
83
|
-
li.append(link)
|
|
84
|
-
list.append(li)
|
|
85
|
-
}
|
|
78
|
+
nav.addEventListener('click', (e) => {
|
|
79
|
+
// Let modified / non-primary clicks (open-in-new-tab etc.) use the native href.
|
|
80
|
+
if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) {
|
|
81
|
+
return
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
firstLink?.focus()
|
|
93
|
-
})
|
|
84
|
+
const link = (e.target as HTMLElement).closest<HTMLElement>(
|
|
85
|
+
'[data-skip-region]',
|
|
86
|
+
)
|
|
87
|
+
if (!link) return
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
const layer = focusRegionManager.getTopLayer()
|
|
90
|
+
const config = focusRegionManager
|
|
91
|
+
.getVisibleRegions(layer)
|
|
92
|
+
.find((region) => region.id === link.dataset.skipRegion)
|
|
93
|
+
|
|
94
|
+
if (config) {
|
|
95
|
+
e.preventDefault()
|
|
96
|
+
focusRegionManager.focusRegion(config, layer)
|
|
97
|
+
return
|
|
101
98
|
}
|
|
102
|
-
})
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
// The region is gone. A dynamic link must not fall through to its native href — that
|
|
101
|
+
// would jump to a now-hidden target and drop focus. Move focus to a stable static link
|
|
102
|
+
// first (so removing the clicked link can't drop focus to <body>), then refresh.
|
|
103
|
+
if (link.closest('[data-skip-dynamic]')) {
|
|
104
|
+
e.preventDefault()
|
|
105
|
+
list.querySelector<HTMLElement>('.SkipLinks-link')?.focus()
|
|
106
|
+
refreshDynamicLinks()
|
|
110
107
|
}
|
|
108
|
+
// A static link with no live region (no-JS edge) falls through to its native href.
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Rebuild whenever the menu is opened from outside, so it reflects the regions visible
|
|
112
|
+
// now. The lastKey check makes this a no-op when nothing changed, so a plain re-entry
|
|
113
|
+
// (including Shift+Tab onto a dynamic link) doesn't disturb focus.
|
|
114
|
+
nav.addEventListener('focusin', (e) => {
|
|
115
|
+
if (nav.contains(e.relatedTarget as Node)) return
|
|
116
|
+
refreshDynamicLinks()
|
|
111
117
|
})
|
|
118
|
+
|
|
119
|
+
// rAF: regions register asynchronously via their own onFind hooks.
|
|
120
|
+
requestAnimationFrame(refreshDynamicLinks)
|
|
112
121
|
})
|