@adea-ai/ui 0.85.0 → 0.87.0

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.
Files changed (229) hide show
  1. package/README.md +11 -1
  2. package/dist/NOTICE +34 -0
  3. package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
  4. package/dist/components/composites/action-button/action-button.js +2 -2
  5. package/dist/components/composites/action-button/action-button.js.map +1 -1
  6. package/dist/components/composites/list-row/list-row.d.ts +6 -3
  7. package/dist/components/composites/list-row/list-row.d.ts.map +1 -1
  8. package/dist/components/composites/list-row/list-row.js +69 -7
  9. package/dist/components/composites/list-row/list-row.js.map +1 -1
  10. package/dist/components/composites/settings/settings.d.ts +9 -11
  11. package/dist/components/composites/settings/settings.d.ts.map +1 -1
  12. package/dist/components/composites/settings/settings.js +48 -33
  13. package/dist/components/composites/settings/settings.js.map +1 -1
  14. package/dist/components/conversation/message-composer.js +1 -1
  15. package/dist/components/conversation/scroll-follow.d.ts.map +1 -1
  16. package/dist/components/conversation/scroll-follow.js +95 -15
  17. package/dist/components/conversation/scroll-follow.js.map +1 -1
  18. package/dist/components/layout/app-shell/index.d.ts +1 -0
  19. package/dist/components/layout/app-shell/index.d.ts.map +1 -1
  20. package/dist/components/layout/app-shell/index.js +2 -1
  21. package/dist/components/layout/app-shell/skip-link.d.ts +11 -0
  22. package/dist/components/layout/app-shell/skip-link.d.ts.map +1 -0
  23. package/dist/components/layout/app-shell/skip-link.js +44 -0
  24. package/dist/components/layout/app-shell/skip-link.js.map +1 -0
  25. package/dist/components/layout/floating-preview/floating-preview.d.ts +12 -0
  26. package/dist/components/layout/floating-preview/floating-preview.d.ts.map +1 -0
  27. package/dist/components/layout/floating-preview/floating-preview.js +283 -0
  28. package/dist/components/layout/floating-preview/floating-preview.js.map +1 -0
  29. package/dist/components/layout/floating-preview/index.d.ts +3 -0
  30. package/dist/components/layout/floating-preview/index.d.ts.map +1 -0
  31. package/dist/components/layout/floating-preview/index.js +3 -0
  32. package/dist/components/layout/floating-preview/model.d.ts +74 -0
  33. package/dist/components/layout/floating-preview/model.d.ts.map +1 -0
  34. package/dist/components/layout/floating-preview/model.js +146 -0
  35. package/dist/components/layout/floating-preview/model.js.map +1 -0
  36. package/dist/components/theme/index.d.ts +1 -0
  37. package/dist/components/theme/index.d.ts.map +1 -1
  38. package/dist/components/theme/index.js +2 -1
  39. package/dist/components/theme/theme-mode-toggle.d.ts +10 -0
  40. package/dist/components/theme/theme-mode-toggle.d.ts.map +1 -0
  41. package/dist/components/theme/theme-mode-toggle.js +82 -0
  42. package/dist/components/theme/theme-mode-toggle.js.map +1 -0
  43. package/dist/components/ui/checkbox/checkbox.d.ts +2 -0
  44. package/dist/components/ui/checkbox/checkbox.d.ts.map +1 -1
  45. package/dist/components/ui/checkbox/checkbox.js +24 -3
  46. package/dist/components/ui/checkbox/checkbox.js.map +1 -1
  47. package/dist/components/ui/combobox/combobox.d.ts.map +1 -1
  48. package/dist/components/ui/combobox/combobox.js +41 -4
  49. package/dist/components/ui/combobox/combobox.js.map +1 -1
  50. package/dist/components/ui/combobox/index.d.ts +1 -0
  51. package/dist/components/ui/combobox/index.d.ts.map +1 -1
  52. package/dist/components/ui/combobox/index.js +2 -1
  53. package/dist/components/ui/combobox/value-combobox.d.ts +19 -0
  54. package/dist/components/ui/combobox/value-combobox.d.ts.map +1 -0
  55. package/dist/components/ui/combobox/value-combobox.js +42 -0
  56. package/dist/components/ui/combobox/value-combobox.js.map +1 -0
  57. package/dist/components/ui/empty/empty-state.d.ts +15 -0
  58. package/dist/components/ui/empty/empty-state.d.ts.map +1 -0
  59. package/dist/components/ui/empty/empty-state.js +96 -0
  60. package/dist/components/ui/empty/empty-state.js.map +1 -0
  61. package/dist/components/ui/empty/index.d.ts +1 -0
  62. package/dist/components/ui/empty/index.d.ts.map +1 -1
  63. package/dist/components/ui/empty/index.js +2 -1
  64. package/dist/components/ui/entity-icon/entity-icon.d.ts +4 -1
  65. package/dist/components/ui/entity-icon/entity-icon.d.ts.map +1 -1
  66. package/dist/components/ui/entity-icon/entity-icon.js +37 -13
  67. package/dist/components/ui/entity-icon/entity-icon.js.map +1 -1
  68. package/dist/components/ui/field/field.d.ts +36 -13
  69. package/dist/components/ui/field/field.d.ts.map +1 -1
  70. package/dist/components/ui/field/field.js +146 -24
  71. package/dist/components/ui/field/field.js.map +1 -1
  72. package/dist/components/ui/field/index.d.ts +3 -1
  73. package/dist/components/ui/field/index.d.ts.map +1 -1
  74. package/dist/components/ui/field/index.js +3 -2
  75. package/dist/components/ui/field/number-field.d.ts +19 -0
  76. package/dist/components/ui/field/number-field.d.ts.map +1 -0
  77. package/dist/components/ui/field/number-field.js +76 -0
  78. package/dist/components/ui/field/number-field.js.map +1 -0
  79. package/dist/components/ui/input/index.d.ts +1 -0
  80. package/dist/components/ui/input/index.d.ts.map +1 -1
  81. package/dist/components/ui/input/index.js +2 -1
  82. package/dist/components/ui/input/input-control.d.ts +11 -0
  83. package/dist/components/ui/input/input-control.d.ts.map +1 -0
  84. package/dist/components/ui/input/input-control.js +24 -0
  85. package/dist/components/ui/input/input-control.js.map +1 -0
  86. package/dist/components/ui/input/input.d.ts.map +1 -1
  87. package/dist/components/ui/input/input.js +56 -28
  88. package/dist/components/ui/input/input.js.map +1 -1
  89. package/dist/components/ui/input-group/index.d.ts +1 -0
  90. package/dist/components/ui/input-group/index.d.ts.map +1 -1
  91. package/dist/components/ui/input-group/index.js +2 -1
  92. package/dist/components/ui/input-group/input-group.d.ts.map +1 -1
  93. package/dist/components/ui/input-group/input-group.js +42 -5
  94. package/dist/components/ui/input-group/input-group.js.map +1 -1
  95. package/dist/components/ui/input-group/secret-input-group.d.ts +11 -0
  96. package/dist/components/ui/input-group/secret-input-group.d.ts.map +1 -0
  97. package/dist/components/ui/input-group/secret-input-group.js +67 -0
  98. package/dist/components/ui/input-group/secret-input-group.js.map +1 -0
  99. package/dist/components/ui/native-select/native-select.d.ts.map +1 -1
  100. package/dist/components/ui/native-select/native-select.js +39 -4
  101. package/dist/components/ui/native-select/native-select.js.map +1 -1
  102. package/dist/components/ui/progress/progress.d.ts.map +1 -1
  103. package/dist/components/ui/progress/progress.js +30 -18
  104. package/dist/components/ui/progress/progress.js.map +1 -1
  105. package/dist/components/ui/radio-group/radio-group.d.ts.map +1 -1
  106. package/dist/components/ui/radio-group/radio-group.js +41 -4
  107. package/dist/components/ui/radio-group/radio-group.js.map +1 -1
  108. package/dist/components/ui/switch/switch.d.ts +2 -0
  109. package/dist/components/ui/switch/switch.d.ts.map +1 -1
  110. package/dist/components/ui/switch/switch.js +24 -3
  111. package/dist/components/ui/switch/switch.js.map +1 -1
  112. package/dist/components/ui/tabs/tabs.js +1 -1
  113. package/dist/components/ui/tabs/tabs.js.map +1 -1
  114. package/dist/components/ui/text-link/index.d.ts +2 -0
  115. package/dist/components/ui/text-link/index.d.ts.map +1 -0
  116. package/dist/components/ui/text-link/index.js +2 -0
  117. package/dist/components/ui/text-link/text-link.d.ts +9 -0
  118. package/dist/components/ui/text-link/text-link.d.ts.map +1 -0
  119. package/dist/components/ui/text-link/text-link.js +30 -0
  120. package/dist/components/ui/text-link/text-link.js.map +1 -0
  121. package/dist/components/ui/textarea/textarea.d.ts.map +1 -1
  122. package/dist/components/ui/textarea/textarea.js +41 -4
  123. package/dist/components/ui/textarea/textarea.js.map +1 -1
  124. package/dist/index.d.ts +3 -0
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +18 -7
  127. package/dist/lib/clipboard.d.ts +3 -0
  128. package/dist/lib/clipboard.d.ts.map +1 -0
  129. package/dist/lib/clipboard.js +35 -0
  130. package/dist/lib/clipboard.js.map +1 -0
  131. package/dist/lib/download.d.ts +3 -0
  132. package/dist/lib/download.d.ts.map +1 -0
  133. package/dist/lib/download.js +31 -0
  134. package/dist/lib/download.js.map +1 -0
  135. package/dist/lib/form-field.d.ts +37 -0
  136. package/dist/lib/form-field.d.ts.map +1 -0
  137. package/dist/lib/form-field.js +47 -0
  138. package/dist/lib/form-field.js.map +1 -0
  139. package/dist/r/app-shell.json +5 -0
  140. package/dist/r/combobox.json +5 -0
  141. package/dist/r/empty.json +10 -1
  142. package/dist/r/field.json +7 -0
  143. package/dist/r/floating-preview.json +30 -0
  144. package/dist/r/input-group.json +6 -0
  145. package/dist/r/input.json +5 -0
  146. package/dist/r/lib.json +15 -0
  147. package/dist/r/list-row.json +1 -0
  148. package/dist/r/registry.json +112 -1
  149. package/dist/r/src/components/composites/action-button/action-button.tsx +5 -1
  150. package/dist/r/src/components/composites/list-row/list-row.tsx +81 -8
  151. package/dist/r/src/components/composites/settings/settings.tsx +67 -37
  152. package/dist/r/src/components/conversation/scroll-follow.ts +109 -15
  153. package/dist/r/src/components/layout/app-shell/index.ts +1 -0
  154. package/dist/r/src/components/layout/app-shell/skip-link.tsx +35 -0
  155. package/dist/r/src/components/layout/floating-preview/floating-preview.tsx +260 -0
  156. package/dist/r/src/components/layout/floating-preview/index.ts +2 -0
  157. package/dist/r/src/components/layout/floating-preview/model.ts +285 -0
  158. package/dist/r/src/components/theme/index.ts +1 -0
  159. package/dist/r/src/components/theme/theme-mode-toggle.tsx +56 -0
  160. package/dist/r/src/components/ui/checkbox/checkbox.tsx +20 -3
  161. package/dist/r/src/components/ui/combobox/combobox.tsx +24 -1
  162. package/dist/r/src/components/ui/combobox/index.ts +1 -0
  163. package/dist/r/src/components/ui/combobox/value-combobox.tsx +38 -0
  164. package/dist/r/src/components/ui/empty/empty-state.tsx +71 -0
  165. package/dist/r/src/components/ui/empty/index.ts +1 -0
  166. package/dist/r/src/components/ui/entity-icon/entity-icon.tsx +33 -2
  167. package/dist/r/src/components/ui/field/field.tsx +102 -5
  168. package/dist/r/src/components/ui/field/index.ts +5 -0
  169. package/dist/r/src/components/ui/field/number-field.tsx +71 -0
  170. package/dist/r/src/components/ui/input/index.ts +1 -0
  171. package/dist/r/src/components/ui/input/input-control.tsx +38 -0
  172. package/dist/r/src/components/ui/input/input.tsx +32 -20
  173. package/dist/r/src/components/ui/input-group/index.ts +1 -0
  174. package/dist/r/src/components/ui/input-group/input-group.tsx +25 -2
  175. package/dist/r/src/components/ui/input-group/secret-input-group.tsx +58 -0
  176. package/dist/r/src/components/ui/native-select/native-select.tsx +27 -1
  177. package/dist/r/src/components/ui/progress/progress.tsx +12 -8
  178. package/dist/r/src/components/ui/radio-group/radio-group.tsx +30 -2
  179. package/dist/r/src/components/ui/switch/switch.tsx +20 -3
  180. package/dist/r/src/components/ui/tabs/tabs.tsx +1 -1
  181. package/dist/r/src/components/ui/text-link/index.ts +2 -0
  182. package/dist/r/src/components/ui/text-link/text-link.tsx +28 -0
  183. package/dist/r/src/components/ui/textarea/textarea.tsx +24 -1
  184. package/dist/r/src/lib/clipboard.ts +41 -0
  185. package/dist/r/src/lib/download.ts +29 -0
  186. package/dist/r/src/lib/form-field.ts +93 -0
  187. package/dist/r/text-link.json +22 -0
  188. package/dist/r/theming.json +6 -0
  189. package/package.json +1 -1
  190. package/registry.json +112 -1
  191. package/src/components/composites/action-button/action-button.tsx +5 -1
  192. package/src/components/composites/list-row/list-row.tsx +81 -8
  193. package/src/components/composites/settings/settings.tsx +67 -37
  194. package/src/components/conversation/scroll-follow.ts +109 -15
  195. package/src/components/layout/app-shell/index.ts +1 -0
  196. package/src/components/layout/app-shell/skip-link.tsx +35 -0
  197. package/src/components/layout/floating-preview/floating-preview.tsx +260 -0
  198. package/src/components/layout/floating-preview/index.ts +2 -0
  199. package/src/components/layout/floating-preview/model.ts +285 -0
  200. package/src/components/theme/index.ts +1 -0
  201. package/src/components/theme/theme-mode-toggle.tsx +56 -0
  202. package/src/components/ui/checkbox/checkbox.tsx +20 -3
  203. package/src/components/ui/combobox/combobox.tsx +24 -1
  204. package/src/components/ui/combobox/index.ts +1 -0
  205. package/src/components/ui/combobox/value-combobox.tsx +38 -0
  206. package/src/components/ui/empty/empty-state.tsx +71 -0
  207. package/src/components/ui/empty/index.ts +1 -0
  208. package/src/components/ui/entity-icon/entity-icon.tsx +33 -2
  209. package/src/components/ui/field/field.tsx +102 -5
  210. package/src/components/ui/field/index.ts +5 -0
  211. package/src/components/ui/field/number-field.tsx +71 -0
  212. package/src/components/ui/input/index.ts +1 -0
  213. package/src/components/ui/input/input-control.tsx +38 -0
  214. package/src/components/ui/input/input.tsx +32 -20
  215. package/src/components/ui/input-group/index.ts +1 -0
  216. package/src/components/ui/input-group/input-group.tsx +25 -2
  217. package/src/components/ui/input-group/secret-input-group.tsx +58 -0
  218. package/src/components/ui/native-select/native-select.tsx +27 -1
  219. package/src/components/ui/progress/progress.tsx +12 -8
  220. package/src/components/ui/radio-group/radio-group.tsx +30 -2
  221. package/src/components/ui/switch/switch.tsx +20 -3
  222. package/src/components/ui/tabs/tabs.tsx +1 -1
  223. package/src/components/ui/text-link/index.ts +2 -0
  224. package/src/components/ui/text-link/text-link.tsx +28 -0
  225. package/src/components/ui/textarea/textarea.tsx +24 -1
  226. package/src/index.ts +3 -0
  227. package/src/lib/clipboard.ts +41 -0
  228. package/src/lib/download.ts +29 -0
  229. package/src/lib/form-field.ts +93 -0
@@ -0,0 +1,28 @@
1
+ import type { ComponentProps } from 'solid-js'
2
+ import { splitProps } from 'solid-js'
3
+ import { cn } from '../../../lib/utils'
4
+
5
+ /**
6
+ * TextLink.
7
+ *
8
+ * An inline, underlined link for prose and content navigation. Use ActionButton
9
+ * or ListRow when the link is a compact action or a row-sized target.
10
+ */
11
+ export function TextLink(props: ComponentProps<'a'>) {
12
+ const [local, rest] = splitProps(props, ['class', 'tabIndex'])
13
+
14
+ return (
15
+ <a
16
+ data-slot="textlink"
17
+ class={cn(
18
+ 'rounded-sm text-foreground underline decoration-primary underline-offset-4',
19
+ 'transition-colors ease-out hover:decoration-foreground outline-none',
20
+ 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
21
+ 'motion-reduce:transition-none',
22
+ local.class
23
+ )}
24
+ tabIndex={local.tabIndex ?? 0}
25
+ {...rest}
26
+ />
27
+ )
28
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ComponentProps } from 'solid-js'
2
2
  import { splitProps } from 'solid-js'
3
+ import { useFormFieldControl } from '../../../lib/form-field'
3
4
  import { cn } from '../../../lib/utils'
4
5
 
5
6
  /**
@@ -14,11 +15,33 @@ import { cn } from '../../../lib/utils'
14
15
  * remains the fallback so an unsupported engine still gets a usable height.
15
16
  */
16
17
  export function Textarea(props: ComponentProps<'textarea'>) {
17
- const [local, rest] = splitProps(props, ['class'])
18
+ const [local, rest] = splitProps(props, [
19
+ 'class',
20
+ 'id',
21
+ 'aria-label',
22
+ 'aria-labelledby',
23
+ 'aria-describedby',
24
+ 'aria-invalid',
25
+ 'aria-errormessage',
26
+ ])
27
+ const field = useFormFieldControl('control', {
28
+ id: local.id,
29
+ 'aria-label': local['aria-label'],
30
+ 'aria-labelledby': local['aria-labelledby'],
31
+ 'aria-describedby': local['aria-describedby'],
32
+ 'aria-invalid': local['aria-invalid'],
33
+ 'aria-errormessage': local['aria-errormessage'],
34
+ })
18
35
 
19
36
  return (
20
37
  <textarea
21
38
  data-slot="textarea"
39
+ id={field?.id ?? local.id}
40
+ aria-label={local['aria-label']}
41
+ aria-labelledby={field?.['aria-labelledby'] ?? local['aria-labelledby']}
42
+ aria-describedby={field?.['aria-describedby'] ?? local['aria-describedby']}
43
+ aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}
44
+ aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}
22
45
  class={cn(
23
46
  'flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-control-md py-2 text-sm',
24
47
  'transition-[color,box-shadow,border-color] ease-out outline-none',
@@ -0,0 +1,41 @@
1
+ const CLIPBOARD_UNAVAILABLE = 'Clipboard is unavailable in this WebView'
2
+
3
+ /** Writes text with the async Clipboard API, falling back for older WebViews. */
4
+ export async function writeClipboardText(value: string): Promise<void> {
5
+ if (typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function') {
6
+ await navigator.clipboard.writeText(value)
7
+ return
8
+ }
9
+
10
+ if (typeof document === 'undefined' || !document.body) {
11
+ throw new Error(CLIPBOARD_UNAVAILABLE)
12
+ }
13
+
14
+ const previousFocus = document.activeElement
15
+ const textarea = document.createElement('textarea')
16
+ textarea.value = value
17
+ textarea.setAttribute('readonly', '')
18
+ textarea.setAttribute('aria-hidden', 'true')
19
+ textarea.tabIndex = -1
20
+ textarea.style.position = 'fixed'
21
+ textarea.style.opacity = '0'
22
+ document.body.appendChild(textarea)
23
+
24
+ let copied = false
25
+ try {
26
+ textarea.focus({ preventScroll: true })
27
+ textarea.select()
28
+ copied = typeof document.execCommand === 'function' && document.execCommand('copy')
29
+ } finally {
30
+ textarea.remove()
31
+ if (previousFocus instanceof HTMLElement && previousFocus.isConnected) {
32
+ try {
33
+ previousFocus.focus({ preventScroll: true })
34
+ } catch {
35
+ // A host may revoke focus during the copy; preserve the clipboard outcome.
36
+ }
37
+ }
38
+ }
39
+
40
+ if (!copied) throw new Error(CLIPBOARD_UNAVAILABLE)
41
+ }
@@ -0,0 +1,29 @@
1
+ /** Download a Blob through a temporary, non-focusable anchor. */
2
+ export function downloadBlob(blob: Blob, filename: string): void {
3
+ const objectUrl = URL.createObjectURL(blob)
4
+ const previousFocus = document.activeElement as HTMLElement | null
5
+ let anchor: HTMLAnchorElement | undefined
6
+
7
+ try {
8
+ anchor = document.createElement('a')
9
+ anchor.href = objectUrl
10
+ anchor.download = filename
11
+ anchor.hidden = true
12
+ anchor.tabIndex = -1
13
+ anchor.setAttribute('aria-hidden', 'true')
14
+ document.body.append(anchor)
15
+ anchor.click()
16
+ } finally {
17
+ try {
18
+ anchor?.remove()
19
+ } finally {
20
+ try {
21
+ if (previousFocus?.isConnected && document.activeElement !== previousFocus) {
22
+ previousFocus.focus({ preventScroll: true })
23
+ }
24
+ } finally {
25
+ setTimeout(() => URL.revokeObjectURL(objectUrl), 0)
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,93 @@
1
+ import type { ComponentProps } from 'solid-js'
2
+ import { createContext, createSignal, useContext } from 'solid-js'
3
+
4
+ export type FormFieldControlKind = 'control' | 'group'
5
+
6
+ export type FormFieldContextValue = {
7
+ group: () => boolean
8
+ labelId: string
9
+ controlId: () => string
10
+ registerControlId: (requestedId?: string) => string | undefined
11
+ describedBy: () => string | undefined
12
+ errorId: () => string | undefined
13
+ invalid: () => boolean
14
+ }
15
+
16
+ export const FormFieldContext = createContext<FormFieldContextValue>()
17
+
18
+ function mergeIds(...values: Array<string | undefined>) {
19
+ return (
20
+ [...new Set(values.flatMap((value) => value?.split(/\s+/).filter(Boolean) ?? []))].join(' ') ||
21
+ undefined
22
+ )
23
+ }
24
+
25
+ export function createFormFieldContext(props: {
26
+ controlId?: string
27
+ defaultControlId: string
28
+ labelId: string
29
+ descriptionId?: string
30
+ errorId?: string
31
+ hint: () => string | undefined
32
+ error: () => string | undefined
33
+ group: () => boolean
34
+ }): FormFieldContextValue {
35
+ const [controlId, setControlId] = createSignal(props.controlId ?? props.defaultControlId)
36
+
37
+ return {
38
+ group: props.group,
39
+ labelId: props.labelId,
40
+ controlId,
41
+ registerControlId(requestedId) {
42
+ if (props.group() && !requestedId) return undefined
43
+ if (requestedId) {
44
+ setControlId(requestedId)
45
+ return requestedId
46
+ }
47
+ return props.group() ? undefined : controlId()
48
+ },
49
+ describedBy: () =>
50
+ [props.hint() ? props.descriptionId : undefined, props.error() ? props.errorId : undefined]
51
+ .filter((id): id is string => Boolean(id))
52
+ .join(' ') || undefined,
53
+ errorId: () => (props.error() ? props.errorId : undefined),
54
+ invalid: () => Boolean(props.error()),
55
+ }
56
+ }
57
+
58
+ export function useFormFieldControl(
59
+ kind: FormFieldControlKind,
60
+ props: {
61
+ id?: string
62
+ 'aria-label'?: string
63
+ 'aria-labelledby'?: string
64
+ 'aria-describedby'?: string
65
+ 'aria-invalid'?: ComponentProps<'input'>['aria-invalid']
66
+ 'aria-errormessage'?: string
67
+ } = {}
68
+ ) {
69
+ const field = useContext(FormFieldContext)
70
+ if (!field) return null
71
+
72
+ const groupedDescendant = field.group() && kind !== 'group'
73
+ const id = groupedDescendant ? props.id : field.registerControlId(props.id)
74
+ const labelledBy =
75
+ props['aria-label'] || props['aria-labelledby']
76
+ ? props['aria-labelledby']
77
+ : groupedDescendant
78
+ ? undefined
79
+ : field.labelId
80
+ return {
81
+ id,
82
+ 'aria-labelledby': labelledBy,
83
+ get 'aria-describedby'() {
84
+ return mergeIds(props['aria-describedby'], field.describedBy())
85
+ },
86
+ get 'aria-invalid'() {
87
+ return props['aria-invalid'] ?? (field.invalid() || undefined)
88
+ },
89
+ get 'aria-errormessage'() {
90
+ return props['aria-errormessage'] ?? field.errorId()
91
+ },
92
+ }
93
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "text-link",
3
+ "type": "registry:ui",
4
+ "title": "Text Link",
5
+ "description": "",
6
+ "dependencies": [],
7
+ "registryDependencies": [
8
+ "lib"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "src/components/ui/text-link/index.ts",
13
+ "type": "registry:ui",
14
+ "target": "components/ui/text-link/index.ts"
15
+ },
16
+ {
17
+ "path": "src/components/ui/text-link/text-link.tsx",
18
+ "type": "registry:ui",
19
+ "target": "components/ui/text-link/text-link.tsx"
20
+ }
21
+ ]
22
+ }
@@ -10,6 +10,7 @@
10
10
  "@adea-ai/ui/button",
11
11
  "@adea-ai/ui/radio-group",
12
12
  "@adea-ai/ui/toggle-group",
13
+ "@adea-ai/ui/tooltip",
13
14
  "lib"
14
15
  ],
15
16
  "files": [
@@ -23,6 +24,11 @@
23
24
  "type": "registry:ui",
24
25
  "target": "components/theming/index.ts"
25
26
  },
27
+ {
28
+ "path": "src/components/theme/theme-mode-toggle.tsx",
29
+ "type": "registry:ui",
30
+ "target": "components/theming/theme-mode-toggle.tsx"
31
+ },
26
32
  {
27
33
  "path": "src/components/theme/theme-picker.tsx",
28
34
  "type": "registry:ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.85.0",
3
+ "version": "0.87.0",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
package/registry.json CHANGED
@@ -433,6 +433,11 @@
433
433
  "path": "src/components/ui/combobox/index.ts",
434
434
  "type": "registry:ui",
435
435
  "target": "components/ui/combobox/index.ts"
436
+ },
437
+ {
438
+ "path": "src/components/ui/combobox/value-combobox.tsx",
439
+ "type": "registry:ui",
440
+ "target": "components/ui/combobox/value-combobox.tsx"
436
441
  }
437
442
  ]
438
443
  },
@@ -614,11 +619,20 @@
614
619
  "type": "registry:ui",
615
620
  "title": "Empty",
616
621
  "description": "",
617
- "dependencies": [],
622
+ "dependencies": [
623
+ "lucide-solid@^1.48.0"
624
+ ],
618
625
  "registryDependencies": [
626
+ "@adea-ai/ui/action-button",
627
+ "@adea-ai/ui/spinner",
619
628
  "lib"
620
629
  ],
621
630
  "files": [
631
+ {
632
+ "path": "src/components/ui/empty/empty-state.tsx",
633
+ "type": "registry:ui",
634
+ "target": "components/ui/empty/empty-state.tsx"
635
+ },
622
636
  {
623
637
  "path": "src/components/ui/empty/empty.tsx",
624
638
  "type": "registry:ui",
@@ -662,6 +676,8 @@
662
676
  "@kobalte/core@^0.13.14"
663
677
  ],
664
678
  "registryDependencies": [
679
+ "@adea-ai/ui/input",
680
+ "@adea-ai/ui/label",
665
681
  "lib"
666
682
  ],
667
683
  "files": [
@@ -674,6 +690,11 @@
674
690
  "path": "src/components/ui/field/index.ts",
675
691
  "type": "registry:ui",
676
692
  "target": "components/ui/field/index.ts"
693
+ },
694
+ {
695
+ "path": "src/components/ui/field/number-field.tsx",
696
+ "type": "registry:ui",
697
+ "target": "components/ui/field/number-field.tsx"
677
698
  }
678
699
  ]
679
700
  },
@@ -716,6 +737,11 @@
716
737
  "type": "registry:ui",
717
738
  "target": "components/ui/input/index.ts"
718
739
  },
740
+ {
741
+ "path": "src/components/ui/input/input-control.tsx",
742
+ "type": "registry:ui",
743
+ "target": "components/ui/input/input-control.tsx"
744
+ },
719
745
  {
720
746
  "path": "src/components/ui/input/input.tsx",
721
747
  "type": "registry:ui",
@@ -730,6 +756,7 @@
730
756
  "description": "",
731
757
  "dependencies": [],
732
758
  "registryDependencies": [
759
+ "@adea-ai/ui/action-button",
733
760
  "lib"
734
761
  ],
735
762
  "files": [
@@ -742,6 +769,11 @@
742
769
  "path": "src/components/ui/input-group/input-group.tsx",
743
770
  "type": "registry:ui",
744
771
  "target": "components/ui/input-group/input-group.tsx"
772
+ },
773
+ {
774
+ "path": "src/components/ui/input-group/secret-input-group.tsx",
775
+ "type": "registry:ui",
776
+ "target": "components/ui/input-group/secret-input-group.tsx"
745
777
  }
746
778
  ]
747
779
  },
@@ -1315,6 +1347,28 @@
1315
1347
  }
1316
1348
  ]
1317
1349
  },
1350
+ {
1351
+ "name": "text-link",
1352
+ "type": "registry:ui",
1353
+ "title": "Text Link",
1354
+ "description": "",
1355
+ "dependencies": [],
1356
+ "registryDependencies": [
1357
+ "lib"
1358
+ ],
1359
+ "files": [
1360
+ {
1361
+ "path": "src/components/ui/text-link/index.ts",
1362
+ "type": "registry:ui",
1363
+ "target": "components/ui/text-link/index.ts"
1364
+ },
1365
+ {
1366
+ "path": "src/components/ui/text-link/text-link.tsx",
1367
+ "type": "registry:ui",
1368
+ "target": "components/ui/text-link/text-link.tsx"
1369
+ }
1370
+ ]
1371
+ },
1318
1372
  {
1319
1373
  "name": "textarea",
1320
1374
  "type": "registry:ui",
@@ -1454,6 +1508,41 @@
1454
1508
  "path": "src/components/layout/app-shell/index.ts",
1455
1509
  "type": "registry:ui",
1456
1510
  "target": "components/layout/app-shell/index.ts"
1511
+ },
1512
+ {
1513
+ "path": "src/components/layout/app-shell/skip-link.tsx",
1514
+ "type": "registry:ui",
1515
+ "target": "components/layout/app-shell/skip-link.tsx"
1516
+ }
1517
+ ]
1518
+ },
1519
+ {
1520
+ "name": "floating-preview",
1521
+ "type": "registry:ui",
1522
+ "title": "Floating Preview",
1523
+ "description": "",
1524
+ "dependencies": [
1525
+ "lucide-solid@^1.48.0"
1526
+ ],
1527
+ "registryDependencies": [
1528
+ "@adea-ai/ui/action-button",
1529
+ "@adea-ai/ui/scroll-area"
1530
+ ],
1531
+ "files": [
1532
+ {
1533
+ "path": "src/components/layout/floating-preview/floating-preview.tsx",
1534
+ "type": "registry:ui",
1535
+ "target": "components/layout/floating-preview/floating-preview.tsx"
1536
+ },
1537
+ {
1538
+ "path": "src/components/layout/floating-preview/index.ts",
1539
+ "type": "registry:ui",
1540
+ "target": "components/layout/floating-preview/index.ts"
1541
+ },
1542
+ {
1543
+ "path": "src/components/layout/floating-preview/model.ts",
1544
+ "type": "registry:ui",
1545
+ "target": "components/layout/floating-preview/model.ts"
1457
1546
  }
1458
1547
  ]
1459
1548
  },
@@ -1848,6 +1937,7 @@
1848
1937
  "@kobalte/core@^0.13.14"
1849
1938
  ],
1850
1939
  "registryDependencies": [
1940
+ "@adea-ai/ui/tooltip",
1851
1941
  "lib"
1852
1942
  ],
1853
1943
  "files": [
@@ -2095,6 +2185,7 @@
2095
2185
  "@adea-ai/ui/button",
2096
2186
  "@adea-ai/ui/radio-group",
2097
2187
  "@adea-ai/ui/toggle-group",
2188
+ "@adea-ai/ui/tooltip",
2098
2189
  "lib"
2099
2190
  ],
2100
2191
  "files": [
@@ -2108,6 +2199,11 @@
2108
2199
  "type": "registry:ui",
2109
2200
  "target": "components/theming/index.ts"
2110
2201
  },
2202
+ {
2203
+ "path": "src/components/theme/theme-mode-toggle.tsx",
2204
+ "type": "registry:ui",
2205
+ "target": "components/theming/theme-mode-toggle.tsx"
2206
+ },
2111
2207
  {
2112
2208
  "path": "src/components/theme/theme-picker.tsx",
2113
2209
  "type": "registry:ui",
@@ -2195,6 +2291,21 @@
2195
2291
  ],
2196
2292
  "registryDependencies": [],
2197
2293
  "files": [
2294
+ {
2295
+ "path": "src/lib/clipboard.ts",
2296
+ "type": "registry:lib",
2297
+ "target": "lib/clipboard.ts"
2298
+ },
2299
+ {
2300
+ "path": "src/lib/download.ts",
2301
+ "type": "registry:lib",
2302
+ "target": "lib/download.ts"
2303
+ },
2304
+ {
2305
+ "path": "src/lib/form-field.ts",
2306
+ "type": "registry:lib",
2307
+ "target": "lib/form-field.ts"
2308
+ },
2198
2309
  {
2199
2310
  "path": "src/lib/keyed-rows.ts",
2200
2311
  "type": "registry:lib",
@@ -111,7 +111,11 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
111
111
  .join(' '),
112
112
  disabled: disabled() && !local.tooltip,
113
113
  'aria-disabled': disabled() ? true : rest['aria-disabled'],
114
- tabIndex: local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : rest.tabIndex,
114
+ // Make tooltip-bearing native controls explicit tab stops in WebKit configurations that
115
+ // otherwise skip implicit button stops. A caller's tabIndex remains authoritative.
116
+ tabIndex:
117
+ rest.tabIndex ??
118
+ (local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : local.tooltip ? 0 : undefined),
115
119
  role: local.as === 'a' && disabled() ? 'link' : rest.role,
116
120
  href: local.as === 'a' && disabled() ? undefined : (rest as { href?: string }).href,
117
121
  'aria-busy': busy() || local['aria-busy'],
@@ -2,6 +2,7 @@ import type { PolymorphicProps } from '@kobalte/core/polymorphic'
2
2
  import { Polymorphic } from '@kobalte/core/polymorphic'
3
3
  import type { ComponentProps, JSX, ValidComponent } from 'solid-js'
4
4
  import { Show, splitProps } from 'solid-js'
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'
5
6
  import { cn } from '#lib/utils'
6
7
 
7
8
  /**
@@ -18,9 +19,8 @@ import { cn } from '#lib/utils'
18
19
  * arithmetic.
19
20
  *
20
21
  * A row is only interactive when it is given an `as` that can be activated
21
- * (an anchor, a button) or an `onClick`. Without either it renders as a
22
- * `div` with no hover treatment, because a hover state on something that does
23
- * nothing is a lie.
22
+ * (an anchor, a button) or an `onClick`. Click handlers default to a native
23
+ * button; a row without either remains a `div` with no hover treatment.
24
24
  */
25
25
  export type ListRowProps<T extends ValidComponent = 'div'> = PolymorphicProps<
26
26
  T,
@@ -36,30 +36,50 @@ export type ListRowProps<T extends ValidComponent = 'div'> = PolymorphicProps<
36
36
  trailing?: JSX.Element
37
37
  /** A dimmed second line under the main content. */
38
38
  description?: string
39
+ /** A short explanation shown on pointer hover and keyboard focus. */
40
+ tooltip?: string
41
+ /** Native button type; defaults to `button` for button rows. */
42
+ type?: ComponentProps<'button'>['type']
39
43
  }
40
44
  >
41
45
 
42
- export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>) {
46
+ type ListRowBaseProps<T extends ValidComponent = 'div'> = ListRowProps<T> & {
47
+ as?: T
48
+ type?: ComponentProps<'button'>['type']
49
+ }
50
+
51
+ function ListRowBase<T extends ValidComponent = 'div'>(props: ListRowBaseProps<T>) {
43
52
  const [local, rest] = splitProps(props as ListRowProps, [
53
+ 'as',
44
54
  'class',
45
55
  'selected',
46
56
  'dense',
47
57
  'leading',
48
58
  'trailing',
49
59
  'description',
60
+ 'tooltip',
61
+ 'type',
62
+ 'tabIndex',
50
63
  'children',
51
64
  ])
52
65
 
53
66
  return (
54
67
  <Polymorphic
55
- as="div"
68
+ as={(local.as ?? 'div') as T}
69
+ type={local.as === 'button' ? (local.type ?? 'button') : local.type}
70
+ tabIndex={local.tabIndex ?? (local.as === 'button' || local.as === 'a' ? 0 : undefined)}
56
71
  aria-current={local.selected ? 'true' : undefined}
57
72
  data-selected={local.selected ? '' : undefined}
58
73
  class={cn(
59
74
  'group/row flex min-w-0 items-center gap-2.5 rounded-md px-2 text-sm',
60
75
  'transition-colors ease-out outline-none',
61
76
  'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
62
- { 'h-row-sm': local.dense, 'h-row-md': !local.dense },
77
+ {
78
+ 'h-row-sm': local.dense && !local.description,
79
+ 'h-row-md': !local.dense && !local.description,
80
+ 'min-h-row-sm py-1': local.dense && local.description,
81
+ 'min-h-row-md py-1.5': !local.dense && local.description,
82
+ },
63
83
  {
64
84
  'bg-primary-subtle text-foreground': local.selected,
65
85
  'hover:bg-surface-hover': !local.selected,
@@ -76,11 +96,25 @@ export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>
76
96
  <span class="flex min-w-0 flex-1 flex-col">
77
97
  <span class="truncate">{local.children}</span>
78
98
  <Show when={local.description}>
79
- <span class="text-muted-foreground truncate text-xs">{local.description}</span>
99
+ <span
100
+ data-slot="list-row-description"
101
+ class={cn(
102
+ 'truncate text-xs',
103
+ local.selected ? 'text-foreground' : 'text-muted-foreground'
104
+ )}
105
+ >
106
+ {local.description}
107
+ </span>
80
108
  </Show>
81
109
  </span>
82
110
  <Show when={local.trailing}>
83
- <span class="ms-auto flex shrink-0 items-center gap-1 text-muted-foreground">
111
+ <span
112
+ data-slot="list-row-trailing"
113
+ class={cn(
114
+ 'ms-auto flex shrink-0 items-center gap-1',
115
+ local.selected ? 'text-foreground' : 'text-muted-foreground'
116
+ )}
117
+ >
84
118
  {local.trailing}
85
119
  </span>
86
120
  </Show>
@@ -88,6 +122,45 @@ export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>
88
122
  )
89
123
  }
90
124
 
125
+ type ListRowTooltipTargetProps<T extends ValidComponent = 'div'> = Omit<
126
+ ListRowBaseProps<T>,
127
+ 'as'
128
+ > & { rowAs?: T }
129
+
130
+ /** Adapts TooltipTrigger's polymorphic target to ListRow's semantic element. */
131
+ function ListRowTooltipTarget<T extends ValidComponent = 'div'>(
132
+ props: ListRowTooltipTargetProps<T>
133
+ ) {
134
+ const [local, rest] = splitProps(props as ListRowTooltipTargetProps, ['rowAs'])
135
+ return <ListRowBase as={local.rowAs} {...(rest as ListRowBaseProps<T>)} />
136
+ }
137
+
138
+ export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>) {
139
+ const [local, rest] = splitProps(props as ListRowProps, ['as', 'tooltip'])
140
+ const hasClickHandler = () => Boolean(rest.onClick || rest['on:click'])
141
+ const rowAs = () => (local.as ?? (hasClickHandler() ? 'button' : 'div')) as T
142
+ const rowType = () => (rowAs() === 'button' ? (rest.type ?? 'button') : rest.type)
143
+
144
+ return (
145
+ <Show
146
+ when={local.tooltip}
147
+ fallback={<ListRowBase as={rowAs()} type={rowType()} {...(rest as ListRowBaseProps<T>)} />}
148
+ >
149
+ <Tooltip>
150
+ <TooltipTrigger
151
+ as={ListRowTooltipTarget}
152
+ rowAs={rowAs()}
153
+ type={rowType()}
154
+ {...(rest as ListRowTooltipTargetProps<T>)}
155
+ >
156
+ {rest.children}
157
+ </TooltipTrigger>
158
+ <TooltipContent>{local.tooltip}</TooltipContent>
159
+ </Tooltip>
160
+ </Show>
161
+ )
162
+ }
163
+
91
164
  /**
92
165
  * A group of rows with an optional heading and a scrolling body. The heading is
93
166
  * a plain label rather than a control: grouping is for reading, and a