@adea-ai/ui 0.84.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.
- package/README.md +90 -1
- package/dist/NOTICE +60 -0
- package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
- package/dist/components/composites/action-button/action-button.js +2 -2
- package/dist/components/composites/action-button/action-button.js.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts +20 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.js +195 -0
- package/dist/components/composites/catalog-browser/catalog-browser-group.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser.d.ts +3 -0
- package/dist/components/composites/catalog-browser/catalog-browser.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-browser.js +378 -0
- package/dist/components/composites/catalog-browser/catalog-browser.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts +21 -0
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-detail.js +92 -0
- package/dist/components/composites/catalog-browser/catalog-detail.js.map +1 -0
- package/dist/components/composites/catalog-browser/catalog-types.d.ts +78 -0
- package/dist/components/composites/catalog-browser/catalog-types.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/index.d.ts +4 -0
- package/dist/components/composites/catalog-browser/index.d.ts.map +1 -0
- package/dist/components/composites/catalog-browser/index.js +3 -0
- package/dist/components/composites/list-row/list-row.d.ts +6 -3
- package/dist/components/composites/list-row/list-row.d.ts.map +1 -1
- package/dist/components/composites/list-row/list-row.js +69 -7
- package/dist/components/composites/list-row/list-row.js.map +1 -1
- package/dist/components/composites/settings/settings.d.ts +9 -11
- package/dist/components/composites/settings/settings.d.ts.map +1 -1
- package/dist/components/composites/settings/settings.js +48 -33
- package/dist/components/composites/settings/settings.js.map +1 -1
- package/dist/components/conversation/message-composer.js +1 -1
- package/dist/components/conversation/scroll-follow.d.ts.map +1 -1
- package/dist/components/conversation/scroll-follow.js +95 -15
- package/dist/components/conversation/scroll-follow.js.map +1 -1
- package/dist/components/layout/app-shell/index.d.ts +1 -0
- package/dist/components/layout/app-shell/index.d.ts.map +1 -1
- package/dist/components/layout/app-shell/index.js +2 -1
- package/dist/components/layout/app-shell/skip-link.d.ts +11 -0
- package/dist/components/layout/app-shell/skip-link.d.ts.map +1 -0
- package/dist/components/layout/app-shell/skip-link.js +44 -0
- package/dist/components/layout/app-shell/skip-link.js.map +1 -0
- package/dist/components/layout/floating-preview/floating-preview.d.ts +12 -0
- package/dist/components/layout/floating-preview/floating-preview.d.ts.map +1 -0
- package/dist/components/layout/floating-preview/floating-preview.js +283 -0
- package/dist/components/layout/floating-preview/floating-preview.js.map +1 -0
- package/dist/components/layout/floating-preview/index.d.ts +3 -0
- package/dist/components/layout/floating-preview/index.d.ts.map +1 -0
- package/dist/components/layout/floating-preview/index.js +3 -0
- package/dist/components/layout/floating-preview/model.d.ts +74 -0
- package/dist/components/layout/floating-preview/model.d.ts.map +1 -0
- package/dist/components/layout/floating-preview/model.js +146 -0
- package/dist/components/layout/floating-preview/model.js.map +1 -0
- package/dist/components/theme/index.d.ts +1 -0
- package/dist/components/theme/index.d.ts.map +1 -1
- package/dist/components/theme/index.js +2 -1
- package/dist/components/theme/theme-mode-toggle.d.ts +10 -0
- package/dist/components/theme/theme-mode-toggle.d.ts.map +1 -0
- package/dist/components/theme/theme-mode-toggle.js +82 -0
- package/dist/components/theme/theme-mode-toggle.js.map +1 -0
- package/dist/components/ui/checkbox/checkbox.d.ts +2 -0
- package/dist/components/ui/checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/ui/checkbox/checkbox.js +24 -3
- package/dist/components/ui/checkbox/checkbox.js.map +1 -1
- package/dist/components/ui/combobox/combobox.d.ts.map +1 -1
- package/dist/components/ui/combobox/combobox.js +41 -4
- package/dist/components/ui/combobox/combobox.js.map +1 -1
- package/dist/components/ui/combobox/index.d.ts +1 -0
- package/dist/components/ui/combobox/index.d.ts.map +1 -1
- package/dist/components/ui/combobox/index.js +2 -1
- package/dist/components/ui/combobox/value-combobox.d.ts +19 -0
- package/dist/components/ui/combobox/value-combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox/value-combobox.js +42 -0
- package/dist/components/ui/combobox/value-combobox.js.map +1 -0
- package/dist/components/ui/empty/empty-state.d.ts +15 -0
- package/dist/components/ui/empty/empty-state.d.ts.map +1 -0
- package/dist/components/ui/empty/empty-state.js +96 -0
- package/dist/components/ui/empty/empty-state.js.map +1 -0
- package/dist/components/ui/empty/index.d.ts +1 -0
- package/dist/components/ui/empty/index.d.ts.map +1 -1
- package/dist/components/ui/empty/index.js +2 -1
- package/dist/components/ui/entity-icon/entity-icon.d.ts +4 -1
- package/dist/components/ui/entity-icon/entity-icon.d.ts.map +1 -1
- package/dist/components/ui/entity-icon/entity-icon.js +37 -13
- package/dist/components/ui/entity-icon/entity-icon.js.map +1 -1
- package/dist/components/ui/field/field.d.ts +36 -13
- package/dist/components/ui/field/field.d.ts.map +1 -1
- package/dist/components/ui/field/field.js +146 -24
- package/dist/components/ui/field/field.js.map +1 -1
- package/dist/components/ui/field/index.d.ts +3 -1
- package/dist/components/ui/field/index.d.ts.map +1 -1
- package/dist/components/ui/field/index.js +3 -2
- package/dist/components/ui/field/number-field.d.ts +19 -0
- package/dist/components/ui/field/number-field.d.ts.map +1 -0
- package/dist/components/ui/field/number-field.js +76 -0
- package/dist/components/ui/field/number-field.js.map +1 -0
- package/dist/components/ui/input/index.d.ts +1 -0
- package/dist/components/ui/input/index.d.ts.map +1 -1
- package/dist/components/ui/input/index.js +2 -1
- package/dist/components/ui/input/input-control.d.ts +11 -0
- package/dist/components/ui/input/input-control.d.ts.map +1 -0
- package/dist/components/ui/input/input-control.js +24 -0
- package/dist/components/ui/input/input-control.js.map +1 -0
- package/dist/components/ui/input/input.d.ts.map +1 -1
- package/dist/components/ui/input/input.js +56 -28
- package/dist/components/ui/input/input.js.map +1 -1
- package/dist/components/ui/input-group/index.d.ts +1 -0
- package/dist/components/ui/input-group/index.d.ts.map +1 -1
- package/dist/components/ui/input-group/index.js +2 -1
- package/dist/components/ui/input-group/input-group.d.ts.map +1 -1
- package/dist/components/ui/input-group/input-group.js +42 -5
- package/dist/components/ui/input-group/input-group.js.map +1 -1
- package/dist/components/ui/input-group/secret-input-group.d.ts +11 -0
- package/dist/components/ui/input-group/secret-input-group.d.ts.map +1 -0
- package/dist/components/ui/input-group/secret-input-group.js +67 -0
- package/dist/components/ui/input-group/secret-input-group.js.map +1 -0
- package/dist/components/ui/native-select/native-select.d.ts.map +1 -1
- package/dist/components/ui/native-select/native-select.js +39 -4
- package/dist/components/ui/native-select/native-select.js.map +1 -1
- package/dist/components/ui/progress/progress.d.ts.map +1 -1
- package/dist/components/ui/progress/progress.js +30 -18
- package/dist/components/ui/progress/progress.js.map +1 -1
- package/dist/components/ui/radio-group/radio-group.d.ts.map +1 -1
- package/dist/components/ui/radio-group/radio-group.js +41 -4
- package/dist/components/ui/radio-group/radio-group.js.map +1 -1
- package/dist/components/ui/switch/switch.d.ts +2 -0
- package/dist/components/ui/switch/switch.d.ts.map +1 -1
- package/dist/components/ui/switch/switch.js +24 -3
- package/dist/components/ui/switch/switch.js.map +1 -1
- package/dist/components/ui/tabs/tabs.js +1 -1
- package/dist/components/ui/tabs/tabs.js.map +1 -1
- package/dist/components/ui/text-link/index.d.ts +2 -0
- package/dist/components/ui/text-link/index.d.ts.map +1 -0
- package/dist/components/ui/text-link/index.js +2 -0
- package/dist/components/ui/text-link/text-link.d.ts +9 -0
- package/dist/components/ui/text-link/text-link.d.ts.map +1 -0
- package/dist/components/ui/text-link/text-link.js +30 -0
- package/dist/components/ui/text-link/text-link.js.map +1 -0
- package/dist/components/ui/textarea/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea/textarea.js +41 -4
- package/dist/components/ui/textarea/textarea.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -8
- package/dist/lib/clipboard.d.ts +3 -0
- package/dist/lib/clipboard.d.ts.map +1 -0
- package/dist/lib/clipboard.js +35 -0
- package/dist/lib/clipboard.js.map +1 -0
- package/dist/lib/download.d.ts +3 -0
- package/dist/lib/download.d.ts.map +1 -0
- package/dist/lib/download.js +31 -0
- package/dist/lib/download.js.map +1 -0
- package/dist/lib/form-field.d.ts +37 -0
- package/dist/lib/form-field.d.ts.map +1 -0
- package/dist/lib/form-field.js +47 -0
- package/dist/lib/form-field.js.map +1 -0
- package/dist/r/app-shell.json +5 -0
- package/dist/r/catalog-browser.json +47 -0
- package/dist/r/combobox.json +5 -0
- package/dist/r/empty.json +10 -1
- package/dist/r/field.json +7 -0
- package/dist/r/floating-preview.json +30 -0
- package/dist/r/input-group.json +6 -0
- package/dist/r/input.json +5 -0
- package/dist/r/lib.json +15 -0
- package/dist/r/list-row.json +1 -0
- package/dist/r/registry.json +159 -1
- package/dist/r/src/components/composites/action-button/action-button.tsx +5 -1
- package/dist/r/src/components/composites/catalog-browser/catalog-browser-group.tsx +148 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-browser.tsx +258 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-detail.tsx +64 -0
- package/dist/r/src/components/composites/catalog-browser/catalog-types.ts +83 -0
- package/dist/r/src/components/composites/catalog-browser/index.ts +10 -0
- package/dist/r/src/components/composites/list-row/list-row.tsx +81 -8
- package/dist/r/src/components/composites/settings/settings.tsx +67 -37
- package/dist/r/src/components/conversation/scroll-follow.ts +109 -15
- package/dist/r/src/components/layout/app-shell/index.ts +1 -0
- package/dist/r/src/components/layout/app-shell/skip-link.tsx +35 -0
- package/dist/r/src/components/layout/floating-preview/floating-preview.tsx +260 -0
- package/dist/r/src/components/layout/floating-preview/index.ts +2 -0
- package/dist/r/src/components/layout/floating-preview/model.ts +285 -0
- package/dist/r/src/components/theme/index.ts +1 -0
- package/dist/r/src/components/theme/theme-mode-toggle.tsx +56 -0
- package/dist/r/src/components/ui/checkbox/checkbox.tsx +20 -3
- package/dist/r/src/components/ui/combobox/combobox.tsx +24 -1
- package/dist/r/src/components/ui/combobox/index.ts +1 -0
- package/dist/r/src/components/ui/combobox/value-combobox.tsx +38 -0
- package/dist/r/src/components/ui/empty/empty-state.tsx +71 -0
- package/dist/r/src/components/ui/empty/index.ts +1 -0
- package/dist/r/src/components/ui/entity-icon/entity-icon.tsx +33 -2
- package/dist/r/src/components/ui/field/field.tsx +102 -5
- package/dist/r/src/components/ui/field/index.ts +5 -0
- package/dist/r/src/components/ui/field/number-field.tsx +71 -0
- package/dist/r/src/components/ui/input/index.ts +1 -0
- package/dist/r/src/components/ui/input/input-control.tsx +38 -0
- package/dist/r/src/components/ui/input/input.tsx +32 -20
- package/dist/r/src/components/ui/input-group/index.ts +1 -0
- package/dist/r/src/components/ui/input-group/input-group.tsx +25 -2
- package/dist/r/src/components/ui/input-group/secret-input-group.tsx +58 -0
- package/dist/r/src/components/ui/native-select/native-select.tsx +27 -1
- package/dist/r/src/components/ui/progress/progress.tsx +12 -8
- package/dist/r/src/components/ui/radio-group/radio-group.tsx +30 -2
- package/dist/r/src/components/ui/switch/switch.tsx +20 -3
- package/dist/r/src/components/ui/tabs/tabs.tsx +1 -1
- package/dist/r/src/components/ui/text-link/index.ts +2 -0
- package/dist/r/src/components/ui/text-link/text-link.tsx +28 -0
- package/dist/r/src/components/ui/textarea/textarea.tsx +24 -1
- package/dist/r/src/lib/clipboard.ts +41 -0
- package/dist/r/src/lib/download.ts +29 -0
- package/dist/r/src/lib/form-field.ts +93 -0
- package/dist/r/text-link.json +22 -0
- package/dist/r/theming.json +6 -0
- package/package.json +1 -1
- package/registry.json +159 -1
- package/src/components/composites/action-button/action-button.tsx +5 -1
- package/src/components/composites/catalog-browser/catalog-browser-group.tsx +148 -0
- package/src/components/composites/catalog-browser/catalog-browser.tsx +258 -0
- package/src/components/composites/catalog-browser/catalog-detail.tsx +64 -0
- package/src/components/composites/catalog-browser/catalog-types.ts +83 -0
- package/src/components/composites/catalog-browser/index.ts +10 -0
- package/src/components/composites/list-row/list-row.tsx +81 -8
- package/src/components/composites/settings/settings.tsx +67 -37
- package/src/components/conversation/scroll-follow.ts +109 -15
- package/src/components/layout/app-shell/index.ts +1 -0
- package/src/components/layout/app-shell/skip-link.tsx +35 -0
- package/src/components/layout/floating-preview/floating-preview.tsx +260 -0
- package/src/components/layout/floating-preview/index.ts +2 -0
- package/src/components/layout/floating-preview/model.ts +285 -0
- package/src/components/theme/index.ts +1 -0
- package/src/components/theme/theme-mode-toggle.tsx +56 -0
- package/src/components/ui/checkbox/checkbox.tsx +20 -3
- package/src/components/ui/combobox/combobox.tsx +24 -1
- package/src/components/ui/combobox/index.ts +1 -0
- package/src/components/ui/combobox/value-combobox.tsx +38 -0
- package/src/components/ui/empty/empty-state.tsx +71 -0
- package/src/components/ui/empty/index.ts +1 -0
- package/src/components/ui/entity-icon/entity-icon.tsx +33 -2
- package/src/components/ui/field/field.tsx +102 -5
- package/src/components/ui/field/index.ts +5 -0
- package/src/components/ui/field/number-field.tsx +71 -0
- package/src/components/ui/input/index.ts +1 -0
- package/src/components/ui/input/input-control.tsx +38 -0
- package/src/components/ui/input/input.tsx +32 -20
- package/src/components/ui/input-group/index.ts +1 -0
- package/src/components/ui/input-group/input-group.tsx +25 -2
- package/src/components/ui/input-group/secret-input-group.tsx +58 -0
- package/src/components/ui/native-select/native-select.tsx +27 -1
- package/src/components/ui/progress/progress.tsx +12 -8
- package/src/components/ui/radio-group/radio-group.tsx +30 -2
- package/src/components/ui/switch/switch.tsx +20 -3
- package/src/components/ui/tabs/tabs.tsx +1 -1
- package/src/components/ui/text-link/index.ts +2 -0
- package/src/components/ui/text-link/text-link.tsx +28 -0
- package/src/components/ui/textarea/textarea.tsx +24 -1
- package/src/index.ts +4 -0
- package/src/lib/clipboard.ts +41 -0
- package/src/lib/download.ts +29 -0
- package/src/lib/form-field.ts +93 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { createEffect, createMemo, createSignal, For, onCleanup, Show } from 'solid-js'
|
|
2
|
+
import { ArrowLeft, Blocks, Search } from 'lucide-solid'
|
|
3
|
+
import { keyedRows } from '../../../lib/keyed-rows'
|
|
4
|
+
import { Button } from '../../ui/button'
|
|
5
|
+
import {
|
|
6
|
+
Empty,
|
|
7
|
+
EmptyContent,
|
|
8
|
+
EmptyDescription,
|
|
9
|
+
EmptyHeader,
|
|
10
|
+
EmptyMedia,
|
|
11
|
+
EmptyTitle,
|
|
12
|
+
} from '../../ui/empty'
|
|
13
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from '../../ui/input-group'
|
|
14
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../ui/tabs'
|
|
15
|
+
import { ScrollArea } from '../../ui/scroll-area'
|
|
16
|
+
import { cn } from '../../../lib/utils'
|
|
17
|
+
import { CatalogBrowserGroup, CatalogLoadingState } from './catalog-browser-group'
|
|
18
|
+
import type { CatalogBrowserProps } from './catalog-types'
|
|
19
|
+
|
|
20
|
+
export function CatalogBrowser<Value>(props: CatalogBrowserProps<Value>) {
|
|
21
|
+
const [expandedGroups, setExpandedGroups] = createSignal<ReadonlySet<string>>(new Set())
|
|
22
|
+
const [returnKey, setReturnKey] = createSignal<string>()
|
|
23
|
+
const [lastSelectedId, setLastSelectedId] = createSignal<string>()
|
|
24
|
+
const rowRefs = new Map<string, HTMLButtonElement>()
|
|
25
|
+
let searchInput: HTMLInputElement | undefined
|
|
26
|
+
let backButton: HTMLButtonElement | undefined
|
|
27
|
+
|
|
28
|
+
const selectedTab = createMemo(() => props.tabs.find((tab) => tab.id === props.tab))
|
|
29
|
+
const supplemental = () => selectedTab()?.kind === 'supplemental'
|
|
30
|
+
const selected = createMemo(() => {
|
|
31
|
+
const id = props.selectedId
|
|
32
|
+
return id && !supplemental() ? props.entries.find((entry) => entry.id === id) : undefined
|
|
33
|
+
})
|
|
34
|
+
const groupRows = keyedRows(
|
|
35
|
+
() => props.groups,
|
|
36
|
+
(group) => group.id
|
|
37
|
+
)
|
|
38
|
+
const [wide, setWide] = createSignal(
|
|
39
|
+
typeof window === 'undefined' || window.matchMedia('(min-width: 48rem)').matches
|
|
40
|
+
)
|
|
41
|
+
const updateWide = (event: MediaQueryListEvent) => setWide(event.matches)
|
|
42
|
+
|
|
43
|
+
createEffect(() => {
|
|
44
|
+
if (typeof window === 'undefined') return
|
|
45
|
+
const query = window.matchMedia('(min-width: 48rem)')
|
|
46
|
+
query.addEventListener('change', updateWide)
|
|
47
|
+
onCleanup(() => query.removeEventListener('change', updateWide))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
createEffect(() => {
|
|
51
|
+
const open = props.open
|
|
52
|
+
const selectedId = props.selectedId
|
|
53
|
+
const selectedEntry = selected()
|
|
54
|
+
if (!open) {
|
|
55
|
+
setExpandedGroups(new Set<string>())
|
|
56
|
+
setLastSelectedId(undefined)
|
|
57
|
+
setReturnKey(undefined)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if (selectedId && selectedEntry) {
|
|
61
|
+
const previous = lastSelectedId()
|
|
62
|
+
setLastSelectedId(selectedId)
|
|
63
|
+
if (selectedId !== previous) {
|
|
64
|
+
window.requestAnimationFrame(() => {
|
|
65
|
+
if (props.open && props.selectedId === selectedId) backButton?.focus()
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
const previous = lastSelectedId()
|
|
71
|
+
if (!previous) return
|
|
72
|
+
const key = returnKey()
|
|
73
|
+
setLastSelectedId(undefined)
|
|
74
|
+
setReturnKey(undefined)
|
|
75
|
+
window.requestAnimationFrame(() => {
|
|
76
|
+
if (!props.open || props.selectedId) return
|
|
77
|
+
const row = key ? rowRefs.get(key) : undefined
|
|
78
|
+
if (row?.isConnected) row.focus()
|
|
79
|
+
else searchInput?.focus()
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const registerRow = (key: string, element: HTMLButtonElement | undefined) => {
|
|
84
|
+
if (element) rowRefs.set(key, element)
|
|
85
|
+
else rowRefs.delete(key)
|
|
86
|
+
}
|
|
87
|
+
const selectEntry = (key: string, value: Value) => {
|
|
88
|
+
setReturnKey(key)
|
|
89
|
+
props.onSelect(value)
|
|
90
|
+
}
|
|
91
|
+
const toggleGroup = (id: string) => {
|
|
92
|
+
setExpandedGroups((current) => {
|
|
93
|
+
const next = new Set(current)
|
|
94
|
+
if (next.has(id)) next.delete(id)
|
|
95
|
+
else next.add(id)
|
|
96
|
+
return next
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
const previewCount = () => (wide() ? 6 : 3)
|
|
100
|
+
const countLabel = () =>
|
|
101
|
+
props.status === 'loading' ? props.loadingLabel : props.resultLabel(props.resultCount)
|
|
102
|
+
const supplementalContent = () => props.renderSupplementalView?.(props.tab)
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div class={cn('flex min-h-0 min-w-0 flex-1 flex-col', props.class)}>
|
|
106
|
+
<Show
|
|
107
|
+
when={selected()}
|
|
108
|
+
fallback={
|
|
109
|
+
<Tabs
|
|
110
|
+
class="flex min-h-0 min-w-0 flex-1 flex-col gap-0"
|
|
111
|
+
value={props.tab}
|
|
112
|
+
onChange={(value) => value && props.onTabChange(value)}
|
|
113
|
+
>
|
|
114
|
+
<TabsList aria-label={props.tabsLabel} class="ms-auto max-w-full flex-wrap px-4 pt-2">
|
|
115
|
+
<For each={props.tabs}>
|
|
116
|
+
{(tab) => <TabsTrigger value={tab.id}>{tab.label}</TabsTrigger>}
|
|
117
|
+
</For>
|
|
118
|
+
</TabsList>
|
|
119
|
+
<TabsContent value={props.tab} class="flex min-h-0 min-w-0 flex-1 flex-col gap-3">
|
|
120
|
+
<Show
|
|
121
|
+
when={supplemental()}
|
|
122
|
+
fallback={
|
|
123
|
+
<>
|
|
124
|
+
<Show when={props.installError}>
|
|
125
|
+
{(message) => (
|
|
126
|
+
<p class="text-destructive px-4 pt-2 text-sm" role="alert">
|
|
127
|
+
{message()}
|
|
128
|
+
</p>
|
|
129
|
+
)}
|
|
130
|
+
</Show>
|
|
131
|
+
<div class="flex min-w-0 shrink-0 flex-wrap items-center gap-2 px-4 pt-2">
|
|
132
|
+
{props.filterControl}
|
|
133
|
+
<InputGroup class="min-w-0 flex-1 sm:max-w-md">
|
|
134
|
+
<InputGroupAddon>
|
|
135
|
+
<Search aria-hidden="true" />
|
|
136
|
+
</InputGroupAddon>
|
|
137
|
+
<InputGroupInput
|
|
138
|
+
ref={(element) => (searchInput = element)}
|
|
139
|
+
type="search"
|
|
140
|
+
aria-label={props.searchLabel}
|
|
141
|
+
placeholder={props.searchPlaceholder}
|
|
142
|
+
value={props.query}
|
|
143
|
+
onInput={(event) => props.onQueryChange(event.currentTarget.value)}
|
|
144
|
+
/>
|
|
145
|
+
</InputGroup>
|
|
146
|
+
<span class="text-muted-foreground ms-auto text-sm" role="status">
|
|
147
|
+
{countLabel()}
|
|
148
|
+
</span>
|
|
149
|
+
</div>
|
|
150
|
+
<ScrollArea
|
|
151
|
+
role="region"
|
|
152
|
+
aria-label={props.resultsRegionLabel}
|
|
153
|
+
orientation="vertical"
|
|
154
|
+
class="flex min-h-0 min-w-0 flex-1 flex-col gap-5 px-4 pb-4"
|
|
155
|
+
>
|
|
156
|
+
<Show when={props.status === 'error'}>
|
|
157
|
+
<Empty
|
|
158
|
+
class="min-h-0 min-w-0 flex-1"
|
|
159
|
+
role={props.catalogError.role ?? 'alert'}
|
|
160
|
+
>
|
|
161
|
+
<EmptyHeader>
|
|
162
|
+
<EmptyMedia variant="icon">
|
|
163
|
+
<Blocks aria-hidden="true" />
|
|
164
|
+
</EmptyMedia>
|
|
165
|
+
<EmptyTitle>{props.catalogError.title}</EmptyTitle>
|
|
166
|
+
<EmptyDescription>{props.catalogError.description}</EmptyDescription>
|
|
167
|
+
</EmptyHeader>
|
|
168
|
+
<Show when={props.catalogErrorAction}>
|
|
169
|
+
<EmptyContent>{props.catalogErrorAction}</EmptyContent>
|
|
170
|
+
</Show>
|
|
171
|
+
</Empty>
|
|
172
|
+
</Show>
|
|
173
|
+
<Show when={props.status === 'loading'}>
|
|
174
|
+
<CatalogLoadingState label={props.loadingLabel} />
|
|
175
|
+
</Show>
|
|
176
|
+
<Show when={props.status === 'ready'}>
|
|
177
|
+
<Show
|
|
178
|
+
when={props.resultCount > 0}
|
|
179
|
+
fallback={
|
|
180
|
+
<Empty class="min-h-0 min-w-0 flex-1" role="status">
|
|
181
|
+
<EmptyHeader>
|
|
182
|
+
<EmptyMedia variant="icon">
|
|
183
|
+
<Blocks aria-hidden="true" />
|
|
184
|
+
</EmptyMedia>
|
|
185
|
+
<EmptyTitle>{props.emptyState.title}</EmptyTitle>
|
|
186
|
+
<EmptyDescription>{props.emptyState.description}</EmptyDescription>
|
|
187
|
+
</EmptyHeader>
|
|
188
|
+
<Show when={props.emptyStateAction}>
|
|
189
|
+
<EmptyContent>{props.emptyStateAction}</EmptyContent>
|
|
190
|
+
</Show>
|
|
191
|
+
</Empty>
|
|
192
|
+
}
|
|
193
|
+
>
|
|
194
|
+
<div class="grid min-w-0 gap-5">
|
|
195
|
+
<For each={groupRows()}>
|
|
196
|
+
{(row) => (
|
|
197
|
+
<CatalogBrowserGroup
|
|
198
|
+
group={row.item()}
|
|
199
|
+
expanded={expandedGroups().has(row.item().id)}
|
|
200
|
+
previewCount={previewCount()}
|
|
201
|
+
disabled={props.interactionDisabled ?? false}
|
|
202
|
+
installedLabel={props.installedLabel}
|
|
203
|
+
publishedByLabel={props.publishedByLabel}
|
|
204
|
+
showMoreLabel={props.showMoreLabel}
|
|
205
|
+
showLessLabel={props.showLessLabel}
|
|
206
|
+
renderIcon={props.renderIcon}
|
|
207
|
+
onSelect={(entry, key) => selectEntry(key, entry.value)}
|
|
208
|
+
onRef={registerRow}
|
|
209
|
+
onToggle={toggleGroup}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
</For>
|
|
213
|
+
</div>
|
|
214
|
+
</Show>
|
|
215
|
+
</Show>
|
|
216
|
+
<For each={props.notices ?? []}>
|
|
217
|
+
{(notice) => (
|
|
218
|
+
<p class="text-muted-foreground text-sm" role={notice.role}>
|
|
219
|
+
{notice.message}
|
|
220
|
+
</p>
|
|
221
|
+
)}
|
|
222
|
+
</For>
|
|
223
|
+
</ScrollArea>
|
|
224
|
+
</>
|
|
225
|
+
}
|
|
226
|
+
>
|
|
227
|
+
{supplementalContent()}
|
|
228
|
+
</Show>
|
|
229
|
+
</TabsContent>
|
|
230
|
+
</Tabs>
|
|
231
|
+
}
|
|
232
|
+
>
|
|
233
|
+
{(entry) => (
|
|
234
|
+
<div class="flex min-h-0 min-w-0 flex-1 flex-col gap-3" data-catalog-browser-detail>
|
|
235
|
+
<Button
|
|
236
|
+
ref={(element) => (backButton = element)}
|
|
237
|
+
variant="ghost"
|
|
238
|
+
size="sm"
|
|
239
|
+
onClick={() => props.onBack()}
|
|
240
|
+
class="mx-4 mt-2 self-start"
|
|
241
|
+
>
|
|
242
|
+
<ArrowLeft data-icon="inline-start" aria-hidden="true" />
|
|
243
|
+
{props.backLabel}
|
|
244
|
+
</Button>
|
|
245
|
+
<ScrollArea
|
|
246
|
+
role="region"
|
|
247
|
+
aria-label={props.detailRegionLabel}
|
|
248
|
+
orientation="vertical"
|
|
249
|
+
class="min-h-0 min-w-0 flex-1 px-4 pb-4"
|
|
250
|
+
>
|
|
251
|
+
{props.renderDetail(entry().value)}
|
|
252
|
+
</ScrollArea>
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
</Show>
|
|
256
|
+
</div>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createUniqueId, Show, type JSX } from 'solid-js'
|
|
2
|
+
import { Badge } from '../../ui/badge'
|
|
3
|
+
import { Card, CardContent, CardFooter, CardHeader } from '../../ui/card'
|
|
4
|
+
|
|
5
|
+
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
6
|
+
export function CatalogDetail(props: {
|
|
7
|
+
title: string
|
|
8
|
+
description: string
|
|
9
|
+
category: string
|
|
10
|
+
publisher: string
|
|
11
|
+
publishedByLabel: (publisher: string) => string
|
|
12
|
+
leading?: JSX.Element
|
|
13
|
+
eyebrow?: string
|
|
14
|
+
badges?: JSX.Element
|
|
15
|
+
action?: JSX.Element
|
|
16
|
+
status?: JSX.Element
|
|
17
|
+
children?: JSX.Element
|
|
18
|
+
}) {
|
|
19
|
+
return (
|
|
20
|
+
<article class="grid min-w-0 gap-4" data-catalog-detail>
|
|
21
|
+
<Card>
|
|
22
|
+
<CardHeader class="flex-row items-start gap-3">
|
|
23
|
+
<Show when={props.leading}>
|
|
24
|
+
<span class="shrink-0">{props.leading}</span>
|
|
25
|
+
</Show>
|
|
26
|
+
<div class="grid min-w-0 gap-1.5">
|
|
27
|
+
<div class="flex min-w-0 flex-wrap items-center gap-1.5">
|
|
28
|
+
<Show when={props.eyebrow}>
|
|
29
|
+
<Badge variant="outline">{props.eyebrow}</Badge>
|
|
30
|
+
</Show>
|
|
31
|
+
<Badge variant="secondary">{props.category}</Badge>
|
|
32
|
+
{props.badges}
|
|
33
|
+
</div>
|
|
34
|
+
<h2 class="text-base leading-tight font-semibold tracking-tight">{props.title}</h2>
|
|
35
|
+
<p class="text-muted-foreground text-sm">{props.description}</p>
|
|
36
|
+
<p class="text-muted-foreground text-xs">{props.publishedByLabel(props.publisher)}</p>
|
|
37
|
+
</div>
|
|
38
|
+
</CardHeader>
|
|
39
|
+
<CardFooter class="flex-wrap justify-between gap-3">
|
|
40
|
+
{props.action}
|
|
41
|
+
{props.status}
|
|
42
|
+
</CardFooter>
|
|
43
|
+
</Card>
|
|
44
|
+
<div class="grid min-w-0 gap-3">{props.children}</div>
|
|
45
|
+
</article>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A shared surface and heading for host-specific fields, permissions, and copy. */
|
|
50
|
+
export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
|
|
51
|
+
const headingId = `catalog-detail-section-${createUniqueId()}`
|
|
52
|
+
return (
|
|
53
|
+
<section class="min-w-0" aria-labelledby={headingId}>
|
|
54
|
+
<Card>
|
|
55
|
+
<CardHeader>
|
|
56
|
+
<h3 id={headingId} class="text-sm font-semibold tracking-tight">
|
|
57
|
+
{props.title}
|
|
58
|
+
</h3>
|
|
59
|
+
</CardHeader>
|
|
60
|
+
<CardContent class="grid min-w-0 gap-3">{props.children}</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
</section>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js'
|
|
2
|
+
|
|
3
|
+
/** A host-owned catalog record reduced to the fields needed by the browser. */
|
|
4
|
+
export type CatalogBrowserEntry<Value> = Readonly<{
|
|
5
|
+
id: string
|
|
6
|
+
value: Value
|
|
7
|
+
name: string
|
|
8
|
+
description: string
|
|
9
|
+
category: string
|
|
10
|
+
publisher: string
|
|
11
|
+
installed: boolean
|
|
12
|
+
}>
|
|
13
|
+
|
|
14
|
+
/** One stable, host-ordered category in the browser. */
|
|
15
|
+
export type CatalogBrowserGroup<Value> = Readonly<{
|
|
16
|
+
id: string
|
|
17
|
+
label: string
|
|
18
|
+
entries: readonly CatalogBrowserEntry<Value>[]
|
|
19
|
+
}>
|
|
20
|
+
|
|
21
|
+
export type CatalogBrowserTab = Readonly<{
|
|
22
|
+
id: string
|
|
23
|
+
label: string
|
|
24
|
+
/** A host-rendered view, such as navigation settings, outside the catalog. */
|
|
25
|
+
kind?: 'catalog' | 'supplemental'
|
|
26
|
+
}>
|
|
27
|
+
|
|
28
|
+
export type CatalogBrowserNotice = Readonly<{
|
|
29
|
+
id: string
|
|
30
|
+
role: 'alert' | 'status'
|
|
31
|
+
message: string
|
|
32
|
+
}>
|
|
33
|
+
|
|
34
|
+
export type CatalogBrowserMessage = Readonly<{
|
|
35
|
+
title: string
|
|
36
|
+
description: string
|
|
37
|
+
role?: 'alert' | 'status'
|
|
38
|
+
}>
|
|
39
|
+
|
|
40
|
+
export type CatalogBrowserProps<Value> = Readonly<{
|
|
41
|
+
/** Optional layout hook for the host's containing surface. */
|
|
42
|
+
class?: string
|
|
43
|
+
/** Whether the containing host surface is open; closing it clears view-only state. */
|
|
44
|
+
open: boolean
|
|
45
|
+
tabs: readonly CatalogBrowserTab[]
|
|
46
|
+
tab: string
|
|
47
|
+
tabsLabel: string
|
|
48
|
+
onTabChange: (tab: string) => void
|
|
49
|
+
/** A host-owned view is rendered in the same tab frame without catalog assumptions. */
|
|
50
|
+
renderSupplementalView?: (tab: string) => JSX.Element | undefined
|
|
51
|
+
query: string
|
|
52
|
+
onQueryChange: (query: string) => void
|
|
53
|
+
searchLabel: string
|
|
54
|
+
searchPlaceholder: string
|
|
55
|
+
resultsRegionLabel: string
|
|
56
|
+
filterControl?: JSX.Element
|
|
57
|
+
resultCount: number
|
|
58
|
+
resultLabel: (count: number) => string
|
|
59
|
+
loadingLabel: string
|
|
60
|
+
status: 'loading' | 'ready' | 'error'
|
|
61
|
+
catalogError: CatalogBrowserMessage
|
|
62
|
+
catalogErrorAction?: JSX.Element
|
|
63
|
+
installError?: string
|
|
64
|
+
notices?: readonly CatalogBrowserNotice[]
|
|
65
|
+
emptyState: CatalogBrowserMessage
|
|
66
|
+
emptyStateAction?: JSX.Element
|
|
67
|
+
groups: readonly CatalogBrowserGroup<Value>[]
|
|
68
|
+
/** All selectable records, including records omitted by the current filter. */
|
|
69
|
+
entries: readonly CatalogBrowserEntry<Value>[]
|
|
70
|
+
selectedId: string | null
|
|
71
|
+
onSelect: (value: Value) => void
|
|
72
|
+
onBack: () => void
|
|
73
|
+
backLabel: string
|
|
74
|
+
detailRegionLabel: string
|
|
75
|
+
installedLabel: string
|
|
76
|
+
publishedByLabel: (publisher: string) => string
|
|
77
|
+
showMoreLabel: (entries: readonly CatalogBrowserEntry<Value>[], remainingCount: number) => string
|
|
78
|
+
showLessLabel: string
|
|
79
|
+
/** Temporarily disables catalog controls while a host menu owns interaction. */
|
|
80
|
+
interactionDisabled?: boolean
|
|
81
|
+
renderIcon: (value: Value) => JSX.Element
|
|
82
|
+
renderDetail: (value: Value) => JSX.Element
|
|
83
|
+
}>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CatalogBrowser } from './catalog-browser'
|
|
2
|
+
export { CatalogDetail, CatalogDetailSection } from './catalog-detail'
|
|
3
|
+
export type {
|
|
4
|
+
CatalogBrowserEntry,
|
|
5
|
+
CatalogBrowserGroup,
|
|
6
|
+
CatalogBrowserMessage,
|
|
7
|
+
CatalogBrowserNotice,
|
|
8
|
+
CatalogBrowserProps,
|
|
9
|
+
CatalogBrowserTab,
|
|
10
|
+
} from './catalog-types'
|
|
@@ -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`.
|
|
22
|
-
*
|
|
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
|
-
|
|
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=
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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
|