@atelier-ui/react 0.1.2 → 0.1.7
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/CHANGELOG.md +55 -0
- package/package.json +1 -1
- package/src/lib/combobox/llm-combobox.d.ts.map +1 -1
- package/src/lib/combobox/llm-combobox.js +7 -11
- package/src/lib/combobox/llm-combobox.js.map +1 -1
- package/src/lib/radio-group/llm-radio-group.d.ts.map +1 -1
- package/src/lib/radio-group/llm-radio-group.js +24 -2
- package/src/lib/radio-group/llm-radio-group.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
## 0.1.7 (2026-05-30)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **tooling:** replace @behavior comment marker with typed covers() gate ([ff36fe9](https://github.com/DominikPieper/atelier-ui/commit/ff36fe9))
|
|
6
|
+
- **spec:** AI-readiness metadata + token manifest layer ([d7f6235](https://github.com/DominikPieper/atelier-ui/commit/d7f6235))
|
|
7
|
+
|
|
8
|
+
### ❤️ Thank You
|
|
9
|
+
|
|
10
|
+
- Claude Opus 4.7 (1M context)
|
|
11
|
+
- Claude Opus 4.8 (1M context)
|
|
12
|
+
- Dominik Pieper @DominikPieper
|
|
13
|
+
|
|
14
|
+
## 0.1.6 (2026-05-27)
|
|
15
|
+
|
|
16
|
+
### 🚀 Features
|
|
17
|
+
|
|
18
|
+
- **a11y:** add roving arrow-key navigation to React/Vue radio-group ([0c2ae81](https://github.com/DominikPieper/atelier-ui/commit/0c2ae81))
|
|
19
|
+
- **tooling:** enforce behavioral-parity coverage across framework adapters ([06c1829](https://github.com/DominikPieper/atelier-ui/commit/06c1829))
|
|
20
|
+
|
|
21
|
+
### 🩹 Fixes
|
|
22
|
+
|
|
23
|
+
- **angular:** suppress click events on disabled button ([1639ced](https://github.com/DominikPieper/atelier-ui/commit/1639ced))
|
|
24
|
+
|
|
25
|
+
### ❤️ Thank You
|
|
26
|
+
|
|
27
|
+
- Claude Opus 4.7 (1M context)
|
|
28
|
+
- Dominik Pieper @DominikPieper
|
|
29
|
+
|
|
30
|
+
## 0.1.5 (2026-05-20)
|
|
31
|
+
|
|
32
|
+
This was a version bump only for react to align it with other projects, there were no code changes.
|
|
33
|
+
|
|
34
|
+
## 0.1.4 (2026-05-20)
|
|
35
|
+
|
|
36
|
+
### 🩹 Fixes
|
|
37
|
+
|
|
38
|
+
- **storybook-mcp:** align hosted MCPs with Storybook 10.4 reality ([a043e56](https://github.com/DominikPieper/atelier-ui/commit/a043e56))
|
|
39
|
+
|
|
40
|
+
### ❤️ Thank You
|
|
41
|
+
|
|
42
|
+
- Claude Opus 4.7 (1M context)
|
|
43
|
+
- Dominik Pieper @DominikPieper
|
|
44
|
+
|
|
45
|
+
## 0.1.3 (2026-05-19)
|
|
46
|
+
|
|
47
|
+
### 🩹 Fixes
|
|
48
|
+
|
|
49
|
+
- **lint:** satisfy new typescript-eslint + react-hooks v7.1 rules ([e9713f6](https://github.com/DominikPieper/atelier-ui/commit/e9713f6))
|
|
50
|
+
|
|
51
|
+
### ❤️ Thank You
|
|
52
|
+
|
|
53
|
+
- Claude Opus 4.7 (1M context)
|
|
54
|
+
- Dominik Pieper @DominikPieper
|
|
55
|
+
|
|
1
56
|
## 0.1.2 (2026-04-30)
|
|
2
57
|
|
|
3
58
|
This was a version bump only for react to align it with other projects, there were no code changes.
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-combobox.d.ts","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/combobox/llm-combobox.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,oBAAoB,CAAC;AAE5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"llm-combobox.d.ts","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/combobox/llm-combobox.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,oBAAoB,CAAC;AAE5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,aAAa,EACb,OAAY,EACZ,WAAgB,EAChB,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,IAAI,EACJ,MAAW,GACZ,EAAE,gBAAgB,2CA2MlB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import './llm-combobox.css';
|
|
4
|
-
let nextId = 0;
|
|
5
4
|
/**
|
|
6
5
|
* Filterable autocomplete combobox. Accepts an `options` array and emits the
|
|
7
6
|
* selected option's `value`. The user types to narrow the list; selecting an
|
|
@@ -23,7 +22,8 @@ export function LlmCombobox({ value = '', onValueChange, options = [], placehold
|
|
|
23
22
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
24
23
|
const inputRef = useRef(null);
|
|
25
24
|
const containerRef = useRef(null);
|
|
26
|
-
const
|
|
25
|
+
const inputId = useId();
|
|
26
|
+
const panelId = useId();
|
|
27
27
|
const selectedLabel = useMemo(() => options.find((o) => o.value === value)?.label ?? '', [options, value]);
|
|
28
28
|
const filteredOptions = useMemo(() => {
|
|
29
29
|
if (!query)
|
|
@@ -31,12 +31,7 @@ export function LlmCombobox({ value = '', onValueChange, options = [], placehold
|
|
|
31
31
|
const q = query.toLowerCase();
|
|
32
32
|
return options.filter((o) => o.label.toLowerCase().includes(q));
|
|
33
33
|
}, [options, query]);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (!isOpen) {
|
|
37
|
-
setQuery(selectedLabel);
|
|
38
|
-
}
|
|
39
|
-
}, [selectedLabel, isOpen]);
|
|
34
|
+
const displayValue = isOpen ? query : selectedLabel;
|
|
40
35
|
// Outside-click closes panel
|
|
41
36
|
useEffect(() => {
|
|
42
37
|
if (!isOpen)
|
|
@@ -53,6 +48,7 @@ export function LlmCombobox({ value = '', onValueChange, options = [], placehold
|
|
|
53
48
|
function open() {
|
|
54
49
|
if (disabled || readonly)
|
|
55
50
|
return;
|
|
51
|
+
setQuery(selectedLabel);
|
|
56
52
|
setIsOpen(true);
|
|
57
53
|
setActiveIndex(-1);
|
|
58
54
|
}
|
|
@@ -130,8 +126,8 @@ export function LlmCombobox({ value = '', onValueChange, options = [], placehold
|
|
|
130
126
|
]
|
|
131
127
|
.filter(Boolean)
|
|
132
128
|
.join(' ');
|
|
133
|
-
const activeOptionId = activeIndex >= 0 ? `${
|
|
134
|
-
return (_jsxs("div", { className: classes, ref: containerRef, children: [_jsxs("div", { className: "llm-combobox-wrapper", children: [_jsx("input", { ref: inputRef, className: "llm-combobox-input", type: "text", autoComplete: "off", role: "combobox", id:
|
|
129
|
+
const activeOptionId = activeIndex >= 0 ? `${panelId}-option-${activeIndex}` : undefined;
|
|
130
|
+
return (_jsxs("div", { className: classes, ref: containerRef, children: [_jsxs("div", { className: "llm-combobox-wrapper", children: [_jsx("input", { ref: inputRef, className: "llm-combobox-input", type: "text", autoComplete: "off", role: "combobox", id: inputId, "aria-expanded": isOpen, "aria-controls": panelId, "aria-autocomplete": "list", "aria-activedescendant": activeOptionId, "aria-invalid": invalid || undefined, disabled: disabled, readOnly: readonly, required: required, name: name, placeholder: placeholder, value: displayValue, onChange: handleInput, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeydown }), _jsx("span", { className: "llm-combobox-icon", "aria-hidden": "true", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: _jsx("path", { d: "M2 4l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), isOpen && (_jsx("ul", { id: panelId, className: "llm-combobox-panel", role: "listbox", "aria-labelledby": inputId, children: filteredOptions.length > 0 ? (filteredOptions.map((option, i) => (_jsxs("li", { id: `${panelId}-option-${i}`, role: "option", className: [
|
|
135
131
|
'llm-combobox-option',
|
|
136
132
|
activeIndex === i && 'is-active',
|
|
137
133
|
option.value === value && 'is-selected',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-combobox.js","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/combobox/llm-combobox.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { LlmComboboxOption, LlmComboboxSpec } from '../spec';\nimport './llm-combobox.css';\n\nexport type { LlmComboboxOption };\n\nlet nextId = 0;\n\nexport interface LlmComboboxProps extends LlmComboboxSpec {\n value?: string;\n onValueChange?: (value: string) => void;\n errors?: string[];\n}\n\n/**\n * Filterable autocomplete combobox. Accepts an `options` array and emits the\n * selected option's `value`. The user types to narrow the list; selecting an\n * option commits the value and displays its label.\n *\n * Usage:\n * ```tsx\n * <LlmCombobox\n * value={country}\n * onValueChange={setCountry}\n * options={countryOptions}\n * placeholder=\"Search country…\"\n * />\n * ```\n */\nexport function LlmCombobox({\n value = '',\n onValueChange,\n options = [],\n placeholder = '',\n disabled = false,\n readonly = false,\n invalid = false,\n required = false,\n name,\n errors = [],\n}: LlmComboboxProps) {\n const [query, setQuery] = useState('');\n const [isOpen, setIsOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const ids = useRef({ input: `llm-combobox-input-${nextId}`, panel: `llm-combobox-panel-${nextId++}` });\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? '',\n [options, value],\n );\n\n const filteredOptions = useMemo(() => {\n if (!query) return options;\n const q = query.toLowerCase();\n return options.filter((o) => o.label.toLowerCase().includes(q));\n }, [options, query]);\n\n // Sync display when value changes externally\n useEffect(() => {\n if (!isOpen) {\n setQuery(selectedLabel);\n }\n }, [selectedLabel, isOpen]);\n\n // Outside-click closes panel\n useEffect(() => {\n if (!isOpen) return;\n function handleClick(e: MouseEvent) {\n if (!containerRef.current?.contains(e.target as Node)) {\n setIsOpen(false);\n setQuery(selectedLabel);\n }\n }\n document.addEventListener('click', handleClick);\n return () => document.removeEventListener('click', handleClick);\n }, [isOpen, selectedLabel]);\n\n function open() {\n if (disabled || readonly) return;\n setIsOpen(true);\n setActiveIndex(-1);\n }\n\n function close() {\n setIsOpen(false);\n setActiveIndex(-1);\n }\n\n function selectOption(option: LlmComboboxOption) {\n if (option.disabled || disabled || readonly) return;\n onValueChange?.(option.value);\n setQuery(option.label);\n close();\n inputRef.current?.focus();\n }\n\n function handleInput(e: React.ChangeEvent<HTMLInputElement>) {\n const q = e.target.value;\n setQuery(q);\n setActiveIndex(-1);\n if (q === '') onValueChange?.('');\n if (!isOpen) open();\n }\n\n function handleFocus() {\n open();\n }\n\n function handleBlur() {\n setQuery(selectedLabel);\n }\n\n function handleKeydown(e: React.KeyboardEvent) {\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n if (!isOpen) { open(); return; }\n setActiveIndex((i) => (i + 1 >= filteredOptions.length ? 0 : i + 1));\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n if (!isOpen) { open(); return; }\n setActiveIndex((i) => (i - 1 < 0 ? filteredOptions.length - 1 : i - 1));\n break;\n }\n case 'Enter': {\n e.preventDefault();\n if (isOpen && activeIndex >= 0 && activeIndex < filteredOptions.length) {\n selectOption(filteredOptions[activeIndex]);\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n setQuery(selectedLabel);\n close();\n break;\n }\n case 'Tab': {\n close();\n break;\n }\n }\n }\n\n const classes = [\n 'llm-combobox',\n isOpen && 'is-open',\n disabled && 'is-disabled',\n invalid && 'is-invalid',\n ]\n .filter(Boolean)\n .join(' ');\n\n const activeOptionId =\n activeIndex >= 0 ? `${ids.current.panel}-option-${activeIndex}` : undefined;\n\n return (\n <div className={classes} ref={containerRef}>\n <div className=\"llm-combobox-wrapper\">\n <input\n ref={inputRef}\n className=\"llm-combobox-input\"\n type=\"text\"\n autoComplete=\"off\"\n role=\"combobox\"\n id={ids.current.input}\n aria-expanded={isOpen}\n aria-controls={ids.current.panel}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOptionId}\n aria-invalid={invalid || undefined}\n disabled={disabled}\n readOnly={readonly}\n required={required}\n name={name}\n placeholder={placeholder}\n value={query}\n onChange={handleInput}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeydown}\n />\n <span className=\"llm-combobox-icon\" aria-hidden=\"true\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M2 4l4 4 4-4\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </span>\n </div>\n\n {isOpen && (\n <ul\n id={ids.current.panel}\n className=\"llm-combobox-panel\"\n role=\"listbox\"\n aria-labelledby={ids.current.input}\n >\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option, i) => (\n <li\n key={option.value}\n id={`${ids.current.panel}-option-${i}`}\n role=\"option\"\n className={[\n 'llm-combobox-option',\n activeIndex === i && 'is-active',\n option.value === value && 'is-selected',\n option.disabled && 'is-disabled',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-selected={option.value === value}\n aria-disabled={option.disabled || undefined}\n onMouseDown={() => selectOption(option)}\n onMouseEnter={() => setActiveIndex(i)}\n >\n <span>{option.label}</span>\n {option.value === value && (\n <svg className=\"llm-combobox-check\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2 7l4 4 6-7\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </li>\n ))\n ) : (\n <li className=\"llm-combobox-no-results\" role=\"option\" aria-selected={false} aria-disabled=\"true\">\n No results found.\n </li>\n )}\n </ul>\n )}\n\n {invalid && errors.length > 0 && (\n <div className=\"llm-combobox-errors\" role=\"alert\" aria-live=\"polite\">\n {errors.map((e, i) => (\n <p key={i} className=\"llm-combobox-error-message\">{e}</p>\n ))}\n </div>\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"llm-combobox.js","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/combobox/llm-combobox.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from 'react';\nimport type { LlmComboboxOption, LlmComboboxSpec } from '../spec';\nimport './llm-combobox.css';\n\nexport type { LlmComboboxOption };\n\nexport interface LlmComboboxProps extends LlmComboboxSpec {\n value?: string;\n onValueChange?: (value: string) => void;\n errors?: string[];\n}\n\n/**\n * Filterable autocomplete combobox. Accepts an `options` array and emits the\n * selected option's `value`. The user types to narrow the list; selecting an\n * option commits the value and displays its label.\n *\n * Usage:\n * ```tsx\n * <LlmCombobox\n * value={country}\n * onValueChange={setCountry}\n * options={countryOptions}\n * placeholder=\"Search country…\"\n * />\n * ```\n */\nexport function LlmCombobox({\n value = '',\n onValueChange,\n options = [],\n placeholder = '',\n disabled = false,\n readonly = false,\n invalid = false,\n required = false,\n name,\n errors = [],\n}: LlmComboboxProps) {\n const [query, setQuery] = useState('');\n const [isOpen, setIsOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const inputId = useId();\n const panelId = useId();\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? '',\n [options, value],\n );\n\n const filteredOptions = useMemo(() => {\n if (!query) return options;\n const q = query.toLowerCase();\n return options.filter((o) => o.label.toLowerCase().includes(q));\n }, [options, query]);\n\n const displayValue = isOpen ? query : selectedLabel;\n\n // Outside-click closes panel\n useEffect(() => {\n if (!isOpen) return;\n function handleClick(e: MouseEvent) {\n if (!containerRef.current?.contains(e.target as Node)) {\n setIsOpen(false);\n setQuery(selectedLabel);\n }\n }\n document.addEventListener('click', handleClick);\n return () => document.removeEventListener('click', handleClick);\n }, [isOpen, selectedLabel]);\n\n function open() {\n if (disabled || readonly) return;\n setQuery(selectedLabel);\n setIsOpen(true);\n setActiveIndex(-1);\n }\n\n function close() {\n setIsOpen(false);\n setActiveIndex(-1);\n }\n\n function selectOption(option: LlmComboboxOption) {\n if (option.disabled || disabled || readonly) return;\n onValueChange?.(option.value);\n setQuery(option.label);\n close();\n inputRef.current?.focus();\n }\n\n function handleInput(e: React.ChangeEvent<HTMLInputElement>) {\n const q = e.target.value;\n setQuery(q);\n setActiveIndex(-1);\n if (q === '') onValueChange?.('');\n if (!isOpen) open();\n }\n\n function handleFocus() {\n open();\n }\n\n function handleBlur() {\n setQuery(selectedLabel);\n }\n\n function handleKeydown(e: React.KeyboardEvent) {\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n if (!isOpen) { open(); return; }\n setActiveIndex((i) => (i + 1 >= filteredOptions.length ? 0 : i + 1));\n break;\n }\n case 'ArrowUp': {\n e.preventDefault();\n if (!isOpen) { open(); return; }\n setActiveIndex((i) => (i - 1 < 0 ? filteredOptions.length - 1 : i - 1));\n break;\n }\n case 'Enter': {\n e.preventDefault();\n if (isOpen && activeIndex >= 0 && activeIndex < filteredOptions.length) {\n selectOption(filteredOptions[activeIndex]);\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n setQuery(selectedLabel);\n close();\n break;\n }\n case 'Tab': {\n close();\n break;\n }\n }\n }\n\n const classes = [\n 'llm-combobox',\n isOpen && 'is-open',\n disabled && 'is-disabled',\n invalid && 'is-invalid',\n ]\n .filter(Boolean)\n .join(' ');\n\n const activeOptionId =\n activeIndex >= 0 ? `${panelId}-option-${activeIndex}` : undefined;\n\n return (\n <div className={classes} ref={containerRef}>\n <div className=\"llm-combobox-wrapper\">\n <input\n ref={inputRef}\n className=\"llm-combobox-input\"\n type=\"text\"\n autoComplete=\"off\"\n role=\"combobox\"\n id={inputId}\n aria-expanded={isOpen}\n aria-controls={panelId}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOptionId}\n aria-invalid={invalid || undefined}\n disabled={disabled}\n readOnly={readonly}\n required={required}\n name={name}\n placeholder={placeholder}\n value={displayValue}\n onChange={handleInput}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeydown}\n />\n <span className=\"llm-combobox-icon\" aria-hidden=\"true\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M2 4l4 4 4-4\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </span>\n </div>\n\n {isOpen && (\n <ul\n id={panelId}\n className=\"llm-combobox-panel\"\n role=\"listbox\"\n aria-labelledby={inputId}\n >\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option, i) => (\n <li\n key={option.value}\n id={`${panelId}-option-${i}`}\n role=\"option\"\n className={[\n 'llm-combobox-option',\n activeIndex === i && 'is-active',\n option.value === value && 'is-selected',\n option.disabled && 'is-disabled',\n ]\n .filter(Boolean)\n .join(' ')}\n aria-selected={option.value === value}\n aria-disabled={option.disabled || undefined}\n onMouseDown={() => selectOption(option)}\n onMouseEnter={() => setActiveIndex(i)}\n >\n <span>{option.label}</span>\n {option.value === value && (\n <svg className=\"llm-combobox-check\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2 7l4 4 6-7\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </li>\n ))\n ) : (\n <li className=\"llm-combobox-no-results\" role=\"option\" aria-selected={false} aria-disabled=\"true\">\n No results found.\n </li>\n )}\n </ul>\n )}\n\n {invalid && errors.length > 0 && (\n <div className=\"llm-combobox-errors\" role=\"alert\" aria-live=\"polite\">\n {errors.map((e, i) => (\n <p key={i} className=\"llm-combobox-error-message\">{e}</p>\n ))}\n </div>\n )}\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-radio-group.d.ts","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/radio-group/llm-radio-group.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"llm-radio-group.d.ts","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/radio-group/llm-radio-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,mBAAmB,EACnB,SAAS,EACV,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,uBAAuB,CAAC;AAE/B;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,iDAQ5B,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,EAC3D,iBAAiB;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAU,EACV,aAAa,EACb,WAAwB,EACxB,IAAS,EACT,QAAgB,EAChB,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,YAAY,EACtB,OAAe,EACf,QAAgB,EAChB,MAAW,EACX,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACR,EAAE,kBAAkB,2CAsEpB;AAED,wBAAgB,aAAa,IAAI,sBAAsB,CAEtD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext } from 'react';
|
|
2
|
+
import { createContext, useContext, useRef, } from 'react';
|
|
3
3
|
import './llm-radio-group.css';
|
|
4
4
|
export const RadioGroupContext = createContext({
|
|
5
5
|
value: '',
|
|
@@ -15,6 +15,28 @@ export const RadioGroupContext = createContext({
|
|
|
15
15
|
*/
|
|
16
16
|
export function LlmRadioGroup({ value = '', onValueChange, orientation = 'vertical', name = '', disabled = false, readOnly: reactReadOnly, readonly: specReadOnly, invalid = false, required = false, errors = [], children, className, ...rest }) {
|
|
17
17
|
const readOnly = reactReadOnly ?? specReadOnly ?? false;
|
|
18
|
+
const groupRef = useRef(null);
|
|
19
|
+
// Roving arrow-key navigation per the WAI-ARIA radiogroup pattern: Arrow
|
|
20
|
+
// Down/Right move to the next enabled radio, Up/Left to the previous, both
|
|
21
|
+
// wrapping, and the navigated radio is selected and focused.
|
|
22
|
+
const handleKeyDown = (event) => {
|
|
23
|
+
if (disabled || readOnly)
|
|
24
|
+
return;
|
|
25
|
+
const forward = event.key === 'ArrowDown' || event.key === 'ArrowRight';
|
|
26
|
+
const backward = event.key === 'ArrowUp' || event.key === 'ArrowLeft';
|
|
27
|
+
if (!forward && !backward)
|
|
28
|
+
return;
|
|
29
|
+
const radios = Array.from(groupRef.current?.querySelectorAll('input[type="radio"]') ?? []).filter((radio) => !radio.disabled);
|
|
30
|
+
if (radios.length === 0)
|
|
31
|
+
return;
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
const currentIdx = radios.findIndex((radio) => radio.value === value);
|
|
34
|
+
const start = currentIdx === -1 ? (forward ? -1 : 0) : currentIdx;
|
|
35
|
+
const nextIdx = (start + (forward ? 1 : -1) + radios.length) % radios.length;
|
|
36
|
+
const target = radios[nextIdx];
|
|
37
|
+
target.focus();
|
|
38
|
+
onValueChange?.(target.value);
|
|
39
|
+
};
|
|
18
40
|
const classes = [
|
|
19
41
|
'llm-radio-group',
|
|
20
42
|
`orientation-${orientation}`,
|
|
@@ -33,7 +55,7 @@ export function LlmRadioGroup({ value = '', onValueChange, orientation = 'vertic
|
|
|
33
55
|
invalid,
|
|
34
56
|
onSelect: (v) => !disabled && !readOnly && onValueChange?.(v),
|
|
35
57
|
onBlur: () => undefined,
|
|
36
|
-
}, children: _jsxs("div", { className: classes, role: "radiogroup", "aria-invalid": invalid || undefined, "aria-required": required || undefined, ...rest, children: [children, errors.length > 0 && (_jsx("div", { className: "errors", role: "alert", "aria-live": "polite", children: errors.map((err, i) => (_jsx("p", { className: "error-message", children: err }, i))) }))] }) }));
|
|
58
|
+
}, children: _jsxs("div", { ref: groupRef, className: classes, role: "radiogroup", "aria-invalid": invalid || undefined, "aria-required": required || undefined, onKeyDown: handleKeyDown, ...rest, children: [children, errors.length > 0 && (_jsx("div", { className: "errors", role: "alert", "aria-live": "polite", children: errors.map((err, i) => (_jsx("p", { className: "error-message", children: err }, i))) }))] }) }));
|
|
37
59
|
}
|
|
38
60
|
export function useRadioGroup() {
|
|
39
61
|
return useContext(RadioGroupContext);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-radio-group.js","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/radio-group/llm-radio-group.tsx"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"llm-radio-group.js","sourceRoot":"","sources":["../../../../../../libs/react/src/lib/radio-group/llm-radio-group.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n useContext,\n useRef,\n type KeyboardEvent,\n InputHTMLAttributes,\n ReactNode,\n} from 'react';\nimport type { LlmRadioGroupSpec } from '../spec';\nimport './llm-radio-group.css';\n\n/**\n * Interface for the RadioGroup context.\n */\nexport interface RadioGroupContextValue {\n /**\n * The currently selected value.\n */\n value: string;\n /**\n * The name of the radio group.\n */\n name: string;\n /**\n * Whether the entire radio group is disabled.\n */\n disabled: boolean;\n /**\n * Whether the entire radio group is read-only.\n */\n readOnly: boolean;\n /**\n * Whether the radio group is in an invalid state.\n */\n invalid: boolean;\n /**\n * Callback to select a value.\n */\n onSelect: (value: string) => void;\n /**\n * Callback triggered on blur.\n */\n onBlur: () => void;\n}\n\nexport const RadioGroupContext = createContext<RadioGroupContextValue>({\n value: '',\n name: '',\n disabled: false,\n readOnly: false,\n invalid: false,\n onSelect: () => undefined,\n onBlur: () => undefined,\n});\n\n/**\n * Properties for the LlmRadioGroup component.\n */\nexport interface LlmRadioGroupProps\n extends Omit<InputHTMLAttributes<HTMLDivElement>, 'onChange'>,\n LlmRadioGroupSpec {\n /**\n * The current value of the radio group.\n */\n value?: string;\n /**\n * Callback triggered when the value changes.\n */\n onValueChange?: (value: string) => void;\n /**\n * The layout orientation of the radio group.\n */\n orientation?: 'horizontal' | 'vertical';\n /**\n * The name of the radio group, applied to all child radio buttons.\n */\n name?: string;\n /**\n * Whether the radio group is disabled.\n */\n disabled?: boolean;\n /**\n * Whether the radio group is read-only.\n */\n readOnly?: boolean;\n /**\n * Whether the radio group is in an invalid state.\n */\n invalid?: boolean;\n /**\n * Whether a value is required.\n */\n required?: boolean;\n /**\n * Array of error messages to display.\n */\n errors?: string[];\n /**\n * The radio buttons to be rendered.\n */\n children?: ReactNode;\n}\n\n/**\n * A group of radio buttons.\n */\nexport function LlmRadioGroup({\n value = '',\n onValueChange,\n orientation = 'vertical',\n name = '',\n disabled = false,\n readOnly: reactReadOnly,\n readonly: specReadOnly,\n invalid = false,\n required = false,\n errors = [],\n children,\n className,\n ...rest\n}: LlmRadioGroupProps) {\n const readOnly = reactReadOnly ?? specReadOnly ?? false;\n const groupRef = useRef<HTMLDivElement>(null);\n\n // Roving arrow-key navigation per the WAI-ARIA radiogroup pattern: Arrow\n // Down/Right move to the next enabled radio, Up/Left to the previous, both\n // wrapping, and the navigated radio is selected and focused.\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (disabled || readOnly) return;\n const forward = event.key === 'ArrowDown' || event.key === 'ArrowRight';\n const backward = event.key === 'ArrowUp' || event.key === 'ArrowLeft';\n if (!forward && !backward) return;\n const radios = Array.from(\n groupRef.current?.querySelectorAll<HTMLInputElement>('input[type=\"radio\"]') ?? []\n ).filter((radio) => !radio.disabled);\n if (radios.length === 0) return;\n event.preventDefault();\n const currentIdx = radios.findIndex((radio) => radio.value === value);\n const start = currentIdx === -1 ? (forward ? -1 : 0) : currentIdx;\n const nextIdx = (start + (forward ? 1 : -1) + radios.length) % radios.length;\n const target = radios[nextIdx];\n target.focus();\n onValueChange?.(target.value);\n };\n\n const classes = [\n 'llm-radio-group',\n `orientation-${orientation}`,\n invalid && 'is-invalid',\n disabled && 'is-disabled',\n readOnly && 'is-readonly',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <RadioGroupContext.Provider\n value={{\n value,\n name,\n disabled,\n readOnly,\n invalid,\n onSelect: (v) => !disabled && !readOnly && onValueChange?.(v),\n onBlur: () => undefined,\n }}\n >\n <div\n ref={groupRef}\n className={classes}\n role=\"radiogroup\"\n aria-invalid={invalid || undefined}\n aria-required={required || undefined}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {children}\n {errors.length > 0 && (\n <div className=\"errors\" role=\"alert\" aria-live=\"polite\">\n {errors.map((err, i) => (\n <p key={i} className=\"error-message\">\n {err}\n </p>\n ))}\n </div>\n )}\n </div>\n </RadioGroupContext.Provider>\n );\n}\n\nexport function useRadioGroup(): RadioGroupContextValue {\n return useContext(RadioGroupContext);\n}\n"]}
|