@unabridged/midwest 0.22.1 → 0.23.1
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/README.md +1 -1
- package/app/assets/javascript/midwest/index.ts +8 -0
- package/app/assets/javascript/midwest/keyboard_navigation.ts +68 -0
- package/app/assets/javascript/midwest/markdown.ts +79 -0
- package/app/assets/javascript/midwest.js +795 -39
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest/utilities.css +4 -0
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +13 -1
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +8 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/keyboard_navigation.js +42 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/keyboard_navigation.js.map +1 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/markdown.js +52 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/markdown.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/command_palette_component/command_palette_component_controller.js +270 -2
- package/dist/javascript/collection/app/components/midwest/command_palette_component/command_palette_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js +8 -37
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/focus_trap_component/focus_trap_component_controller.js +83 -0
- package/dist/javascript/collection/app/components/midwest/focus_trap_component/focus_trap_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/switch_component/theme_switch_controller.js +42 -0
- package/dist/javascript/collection/app/components/midwest/form/switch_component/theme_switch_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/intersection_observer_component/intersection_observer_component_controller.js +43 -0
- package/dist/javascript/collection/app/components/midwest/intersection_observer_component/intersection_observer_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js +273 -0
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js.map +1 -0
- package/dist/javascript/midwest.js +795 -39
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://github.com/unabridged/midwest/actions/workflows/rails-tests.yml)
|
|
4
4
|
[](https://github.com/unabridged/midwest/actions/workflows/bridgetown-tests.yml)
|
|
5
5
|
[](https://github.com/unabridged/midwest/actions/workflows/ci.yml)
|
|
6
|
-
[](https://github.com/unabridged/midwest/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
8
|
Midwest Component Library is an implementation of the Midwest Design System using [ViewComponent](https://github.com/github/view_component).
|
|
9
9
|
|
|
@@ -32,6 +32,10 @@ import PasswordRequirements from '../../../components/midwest/password_requireme
|
|
|
32
32
|
import CodeBlock from '../../../components/midwest/code_block_component/code_block_component_controller'
|
|
33
33
|
import DatePicker from '../../../components/midwest/form/date_picker_component/date_picker_component_controller'
|
|
34
34
|
import TimePicker from '../../../components/midwest/form/time_picker_component/time_picker_component_controller'
|
|
35
|
+
import IntersectionObserver from '../../../components/midwest/intersection_observer_component/intersection_observer_component_controller'
|
|
36
|
+
import FocusTrap from '../../../components/midwest/focus_trap_component/focus_trap_component_controller'
|
|
37
|
+
import Onboarding from '../../../components/midwest/onboarding_component/onboarding_component_controller'
|
|
38
|
+
import ThemeSwitch from '../../../components/midwest/form/switch_component/theme_switch_controller'
|
|
35
39
|
/* IMPORTS */
|
|
36
40
|
|
|
37
41
|
export { Card, Banner, CountdownTimer, Chart }
|
|
@@ -71,5 +75,9 @@ export function registerMidwestControllers (application: any) {
|
|
|
71
75
|
application.register('midwest-code-block', CodeBlock)
|
|
72
76
|
application.register('midwest-date-picker', DatePicker)
|
|
73
77
|
application.register('midwest-time-picker', TimePicker)
|
|
78
|
+
application.register('midwest-intersection-observer', IntersectionObserver)
|
|
79
|
+
application.register('midwest-focus-trap', FocusTrap)
|
|
80
|
+
application.register('midwest-onboarding', Onboarding)
|
|
81
|
+
application.register('midwest-theme-switch', ThemeSwitch)
|
|
74
82
|
/* EXPORTS */
|
|
75
83
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Shared keyboard-navigation helpers for menu/list components.
|
|
2
|
+
//
|
|
3
|
+
// Implements the WAI-ARIA "roving focus" pattern: arrow keys move DOM focus
|
|
4
|
+
// between a flat list of items, Home/End jump to the ends, and Escape/Tab hand
|
|
5
|
+
// control back to the caller (typically to close a popover and restore focus).
|
|
6
|
+
// Components own *which* elements are navigable and *what* closing means; this
|
|
7
|
+
// module owns the key handling so the behavior stays consistent across the
|
|
8
|
+
// library.
|
|
9
|
+
|
|
10
|
+
const NAVIGATION_KEYS = ['ArrowDown', 'ArrowUp', 'Home', 'End', 'Escape', 'Tab']
|
|
11
|
+
|
|
12
|
+
export interface RovingFocusOptions {
|
|
13
|
+
// Called on Escape. Typically closes the menu and returns focus to the trigger.
|
|
14
|
+
onEscape?: () => void
|
|
15
|
+
// Called on Tab. The default Tab behavior is left intact so focus still leaves
|
|
16
|
+
// the menu; use this to close the menu alongside it.
|
|
17
|
+
onTab?: () => void
|
|
18
|
+
// When true (default) arrow navigation wraps from the last item to the first
|
|
19
|
+
// and vice versa. When false it stops at the ends.
|
|
20
|
+
loop?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handles a keydown against a roving-focus list. Returns true when the event
|
|
24
|
+
// was one this helper is responsible for, so callers can `stopPropagation()`
|
|
25
|
+
// only for keys that were actually handled.
|
|
26
|
+
export function handleRovingFocusKeydown (
|
|
27
|
+
event: KeyboardEvent,
|
|
28
|
+
items: HTMLElement[],
|
|
29
|
+
options: RovingFocusOptions = {}
|
|
30
|
+
): boolean {
|
|
31
|
+
if (items.length === 0) return false
|
|
32
|
+
if (!NAVIGATION_KEYS.includes(event.key)) return false
|
|
33
|
+
|
|
34
|
+
const { onEscape, onTab, loop = true } = options
|
|
35
|
+
const current = items.findIndex(item => item === document.activeElement)
|
|
36
|
+
|
|
37
|
+
switch (event.key) {
|
|
38
|
+
case 'ArrowDown': {
|
|
39
|
+
event.preventDefault()
|
|
40
|
+
const next = current < items.length - 1 ? current + 1 : (loop ? 0 : items.length - 1)
|
|
41
|
+
items[next].focus()
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
case 'ArrowUp': {
|
|
45
|
+
event.preventDefault()
|
|
46
|
+
const prev = current > 0 ? current - 1 : (loop ? items.length - 1 : 0)
|
|
47
|
+
items[prev].focus()
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
case 'Home':
|
|
51
|
+
event.preventDefault()
|
|
52
|
+
items[0].focus()
|
|
53
|
+
break
|
|
54
|
+
case 'End':
|
|
55
|
+
event.preventDefault()
|
|
56
|
+
items[items.length - 1].focus()
|
|
57
|
+
break
|
|
58
|
+
case 'Escape':
|
|
59
|
+
event.preventDefault()
|
|
60
|
+
onEscape?.()
|
|
61
|
+
break
|
|
62
|
+
case 'Tab':
|
|
63
|
+
onTab?.()
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Tiny, dependency-free Markdown renderer for streamed chat responses.
|
|
2
|
+
//
|
|
3
|
+
// It is intentionally a small subset (paragraphs, lists, fenced/inline code,
|
|
4
|
+
// bold, italic, and safe links) rather than a full CommonMark implementation —
|
|
5
|
+
// the command palette streams tokens and cannot afford a heavy parser.
|
|
6
|
+
//
|
|
7
|
+
// Security: model output is untrusted. Text is HTML-escaped BEFORE any markup is
|
|
8
|
+
// applied, and links are restricted to http(s)/mailto, so the result is safe to
|
|
9
|
+
// assign to innerHTML.
|
|
10
|
+
|
|
11
|
+
const PLACEHOLDER = '\uE000' // private-use char, unlikely in real text
|
|
12
|
+
|
|
13
|
+
export function renderMarkdown (src: string): string {
|
|
14
|
+
const blocks: string[] = []
|
|
15
|
+
|
|
16
|
+
// Pull fenced code blocks out first so their contents are never treated as markup.
|
|
17
|
+
const withoutFences = src.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, _lang, code: string) => {
|
|
18
|
+
const body = escapeHtml(code.replace(/\n$/, ''))
|
|
19
|
+
blocks.push(`<pre class="command-palette-code"><code>${body}</code></pre>`)
|
|
20
|
+
return `${PLACEHOLDER}${blocks.length - 1}${PLACEHOLDER}`
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const lines = escapeHtml(withoutFences).split('\n')
|
|
24
|
+
const out: string[] = []
|
|
25
|
+
let listOpen = false
|
|
26
|
+
|
|
27
|
+
const closeList = () => {
|
|
28
|
+
if (listOpen) {
|
|
29
|
+
out.push('</ul>')
|
|
30
|
+
listOpen = false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
const placeholder = line.match(new RegExp(`^${PLACEHOLDER}(\\d+)${PLACEHOLDER}$`))
|
|
36
|
+
if (placeholder) {
|
|
37
|
+
closeList()
|
|
38
|
+
out.push(line)
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const item = line.match(/^\s*[-*]\s+(.*)$/)
|
|
43
|
+
if (item) {
|
|
44
|
+
if (!listOpen) {
|
|
45
|
+
out.push('<ul>')
|
|
46
|
+
listOpen = true
|
|
47
|
+
}
|
|
48
|
+
out.push(`<li>${inline(item[1])}</li>`)
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
closeList()
|
|
53
|
+
if (line.trim() !== '') out.push(`<p>${inline(line)}</p>`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
closeList()
|
|
57
|
+
|
|
58
|
+
return out
|
|
59
|
+
.join('\n')
|
|
60
|
+
.replace(new RegExp(`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`, 'g'), (_match, index: string) => blocks[Number(index)])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function inline (text: string): string {
|
|
64
|
+
return text
|
|
65
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
66
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
67
|
+
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
68
|
+
.replace(
|
|
69
|
+
/\[([^\]]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+)\)/g,
|
|
70
|
+
'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function escapeHtml (text: string): string {
|
|
75
|
+
return text
|
|
76
|
+
.replace(/&/g, '&')
|
|
77
|
+
.replace(/</g, '<')
|
|
78
|
+
.replace(/>/g, '>')
|
|
79
|
+
}
|