@lexibase/rdx-ui 3.1.0 → 3.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexibase/rdx-ui",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^22.18.0 || ^24.11.0"
@@ -37,7 +37,7 @@
37
37
  "tailwind-merge": "^3.7.0",
38
38
  "tailwindcss": "^4.3.3",
39
39
  "tw-animate-css": "^1.4.0",
40
- "@lexibase/rdx-types": "1.1.1"
40
+ "@lexibase/rdx-types": "2.0.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "next": "^16.4.2",
@@ -62,9 +62,9 @@ export function DocsSidebar({
62
62
  {categories.map((category, index) => (
63
63
  <div
64
64
  key={`${index}:${category.title}`}
65
- className="flex flex-col gap-0.5 mb-3 first:mt-4"
65
+ className="flex min-w-0 flex-col gap-0.5 mb-3 first:mt-4"
66
66
  >
67
- <p className="text-xs text-ring font-semibold uppercase mb-1">
67
+ <p className="text-xs text-ring font-semibold uppercase mb-1 [overflow-wrap:anywhere]">
68
68
  {category.title}
69
69
  </p>
70
70
  {category.links.map((link) => (
@@ -86,7 +86,7 @@ export function DocsSidebar({
86
86
  }
87
87
  onClick={() => actions.current?.close()}
88
88
  aria-current={pathname === link.href ? 'page' : undefined}
89
- className={`justify-start text-sm ${pathname === link.href ? 'text-accent-foreground' : 'text-primary'}`}
89
+ className={`h-auto min-h-8 w-full min-w-0 justify-start whitespace-normal py-1.5 text-left text-sm [overflow-wrap:anywhere] ${pathname === link.href ? 'text-accent-foreground' : 'text-primary'}`}
90
90
  >
91
91
  {link.label}
92
92
  </Button>
@@ -16,10 +16,17 @@ function Kbd({ className, ...props }: ComponentProps<'kbd'>) {
16
16
  )
17
17
  }
18
18
 
19
- function KbdGroup({ className, ...props }: ComponentProps<'div'>) {
19
+ function KbdGroup({ className, role, ...props }: ComponentProps<'div'>) {
20
20
  return (
21
21
  <kbd
22
22
  data-slot="kbd-group"
23
+ role={
24
+ role ??
25
+ (props['aria-label'] !== undefined ||
26
+ props['aria-labelledby'] !== undefined
27
+ ? 'group'
28
+ : undefined)
29
+ }
23
30
  className={cn('inline-flex items-center gap-1', className)}
24
31
  {...props}
25
32
  />
@@ -29,6 +29,7 @@ import {
29
29
  } from './code-block'
30
30
  import { MdxCodeBlock } from './mdx-code-block'
31
31
  import { Kbd, KbdGroup } from './kbd'
32
+ import { MdxKeyboardShortcut } from './mdx-keyboard-shortcut'
32
33
  import {
33
34
  Table,
34
35
  TableBody,
@@ -154,6 +155,7 @@ export const mdxComponents = {
154
155
  CodeBlockTitle,
155
156
  Kbd,
156
157
  KbdGroup,
158
+ KeyboardShortcut: MdxKeyboardShortcut,
157
159
  Table,
158
160
  TableHeader,
159
161
  TableBody,
@@ -187,6 +189,9 @@ export const mdxComponents = {
187
189
  export function getMdxComponents(lang?: string) {
188
190
  return {
189
191
  ...mdxComponents,
192
+ KeyboardShortcut: (props: { children?: ReactNode }) => (
193
+ <MdxKeyboardShortcut {...props} lang={lang} />
194
+ ),
190
195
  pre: (props: ComponentProps<'pre'>) => (
191
196
  <MdxCodeBlock {...props} lang={lang} />
192
197
  ),
@@ -0,0 +1,74 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { getMessages, type InterfaceMessages } from '../lib/i18n'
4
+ import { Kbd, KbdGroup } from './kbd'
5
+
6
+ const keyNames: Record<string, keyof InterfaceMessages> = {
7
+ ctrl: 'keyboardControl',
8
+ control: 'keyboardControl',
9
+ '⌃': 'keyboardControl',
10
+ shift: 'keyboardShift',
11
+ '⇧': 'keyboardShift',
12
+ alt: 'keyboardAlt',
13
+ cmd: 'keyboardCommand',
14
+ command: 'keyboardCommand',
15
+ '⌘': 'keyboardCommand',
16
+ option: 'keyboardOption',
17
+ '⌥': 'keyboardOption',
18
+ esc: 'keyboardEscape',
19
+ escape: 'keyboardEscape',
20
+ '⎋': 'keyboardEscape',
21
+ enter: 'keyboardEnter',
22
+ return: 'keyboardEnter',
23
+ '↵': 'keyboardEnter',
24
+ '⏎': 'keyboardEnter',
25
+ space: 'keyboardSpace',
26
+ spacebar: 'keyboardSpace',
27
+ '+': 'keyboardPlus',
28
+ arrowup: 'keyboardArrowUp',
29
+ '↑': 'keyboardArrowUp',
30
+ arrowdown: 'keyboardArrowDown',
31
+ '↓': 'keyboardArrowDown',
32
+ arrowleft: 'keyboardArrowLeft',
33
+ '←': 'keyboardArrowLeft',
34
+ arrowright: 'keyboardArrowRight',
35
+ '→': 'keyboardArrowRight',
36
+ }
37
+
38
+ /** Markdown adapter: preserve shadcn visuals, expose one readable text alternative. */
39
+ export function MdxKeyboardShortcut({
40
+ children,
41
+ lang,
42
+ }: {
43
+ children?: ReactNode
44
+ lang?: string
45
+ }) {
46
+ if (typeof children !== 'string') return <Kbd>{children}</Kbd>
47
+ const keys = children
48
+ .trim()
49
+ .split(/\s+\+\s+/)
50
+ .map((key) => key.trim())
51
+ if (keys.length < 2 || keys.some((key) => !key)) return <Kbd>{children}</Kbd>
52
+ const messages = getMessages(lang)
53
+ const description = keys
54
+ .map((key) => {
55
+ const name = Object.hasOwn(keyNames, key.toLowerCase())
56
+ ? keyNames[key.toLowerCase()]
57
+ : undefined
58
+ return name ? messages[name] : key
59
+ })
60
+ .join(messages.keyboardJoiner)
61
+ return (
62
+ <span data-rdx-kbd-value={children} className="inline-flex align-baseline">
63
+ <span className="sr-only select-none">{description}</span>
64
+ <KbdGroup aria-hidden="true">
65
+ {keys.map((key, index) => (
66
+ <span key={index} className="inline-flex items-center gap-1">
67
+ {index > 0 && <span>+</span>}
68
+ <Kbd>{key}</Kbd>
69
+ </span>
70
+ ))}
71
+ </KbdGroup>
72
+ </span>
73
+ )
74
+ }
@@ -0,0 +1,10 @@
1
+ import { getMessages } from '../lib/i18n'
2
+
3
+ /** First keyboard stop, shared by the website and generated documentation. */
4
+ export function SkipLink({ lang }: { lang?: string }) {
5
+ return (
6
+ <a className="rdx-skip-link" href="#rdx-main">
7
+ {getMessages(lang).skipContent}
8
+ </a>
9
+ )
10
+ }
@@ -25,6 +25,7 @@ function panelText(node: Node): string {
25
25
  )
26
26
  )
27
27
  return ''
28
+ if (node.dataset.rdxKbdValue !== undefined) return node.dataset.rdxKbdValue
28
29
  if (node.matches('[data-rdx-code-block], [data-slot="code-block"]')) {
29
30
  const pre = node.querySelector('pre')
30
31
  const lines = pre?.querySelectorAll('[data-slot="code-block-line-content"]')
package/src/lib/i18n.ts CHANGED
@@ -1,5 +1,19 @@
1
1
  /** Shared labels only: document content and author-provided names stay untouched. */
2
2
  const en = {
3
+ keyboardJoiner: ' plus ',
4
+ keyboardControl: 'Control',
5
+ keyboardShift: 'Shift',
6
+ keyboardAlt: 'Alt',
7
+ keyboardCommand: 'Command',
8
+ keyboardOption: 'Option',
9
+ keyboardEscape: 'Escape',
10
+ keyboardEnter: 'Enter',
11
+ keyboardSpace: 'Space',
12
+ keyboardPlus: 'Plus',
13
+ keyboardArrowUp: 'Up arrow',
14
+ keyboardArrowDown: 'Down arrow',
15
+ keyboardArrowLeft: 'Left arrow',
16
+ keyboardArrowRight: 'Right arrow',
3
17
  skipContent: 'Skip to content',
4
18
  mainNavigation: 'Main navigation',
5
19
  docs: 'Docs',
@@ -79,6 +93,20 @@ export type InterfaceMessages = { [Key in keyof typeof en]: string }
79
93
  export type InterfaceLocale = 'en' | 'pt-BR'
80
94
 
81
95
  const ptBR = {
96
+ keyboardJoiner: ' mais ',
97
+ keyboardControl: 'Control',
98
+ keyboardShift: 'Shift',
99
+ keyboardAlt: 'Alt',
100
+ keyboardCommand: 'Command',
101
+ keyboardOption: 'Option',
102
+ keyboardEscape: 'Escape',
103
+ keyboardEnter: 'Enter',
104
+ keyboardSpace: 'Espaço',
105
+ keyboardPlus: 'Sinal de mais',
106
+ keyboardArrowUp: 'Seta para cima',
107
+ keyboardArrowDown: 'Seta para baixo',
108
+ keyboardArrowLeft: 'Seta para a esquerda',
109
+ keyboardArrowRight: 'Seta para a direita',
82
110
  skipContent: 'Ir para o conteúdo',
83
111
  mainNavigation: 'Navegação principal',
84
112
  docs: 'Docs',
@@ -6,6 +6,28 @@
6
6
 
7
7
  @custom-variant dark (&:is(.dark *));
8
8
 
9
+ .rdx-skip-link {
10
+ position: fixed;
11
+ top: 1rem;
12
+ inset-inline-start: 1rem;
13
+ z-index: 100;
14
+ max-width: calc(100vw - 2rem);
15
+ transform: translateY(calc(-100% - 2rem));
16
+ padding: 0.5rem 0.75rem;
17
+ border: 1px solid var(--border);
18
+ border-radius: var(--radius);
19
+ background: var(--background);
20
+ color: var(--foreground);
21
+ font-size: 0.875rem;
22
+ font-weight: 500;
23
+ outline: 2px solid var(--ring);
24
+ outline-offset: 2px;
25
+ }
26
+
27
+ .rdx-skip-link:focus {
28
+ transform: translateY(0);
29
+ }
30
+
9
31
  :root {
10
32
  --background: oklch(1 0 0);
11
33
  --foreground: oklch(0.145 0 0);