@proteos/ui 0.31.0 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/index.cjs +1302 -516
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +259 -1
  4. package/dist/index.d.ts +259 -1
  5. package/dist/index.js +1292 -520
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/components/draft-message-composer/create-draft-message-client.ts +27 -0
  9. package/src/components/draft-message-composer/draft-message-actions-store.test.ts +62 -0
  10. package/src/components/draft-message-composer/draft-message-actions-store.ts +81 -0
  11. package/src/components/draft-message-composer/draft-message-composer.stories.tsx +175 -0
  12. package/src/components/draft-message-composer/draft-message-composer.test.tsx +276 -0
  13. package/src/components/draft-message-composer/draft-message-composer.tsx +470 -0
  14. package/src/components/draft-message-composer/draft-message-outcome-store.test.ts +59 -0
  15. package/src/components/draft-message-composer/draft-message-outcome-store.ts +56 -0
  16. package/src/components/draft-message-composer/index.ts +19 -0
  17. package/src/components/draft-message-composer/types.ts +32 -0
  18. package/src/components/message-composer/index.ts +13 -0
  19. package/src/components/message-composer/message-composer.stories.tsx +155 -0
  20. package/src/components/message-composer/message-composer.test.tsx +320 -0
  21. package/src/components/message-composer/message-composer.tsx +480 -0
  22. package/src/components/recipient-select/index.ts +2 -0
  23. package/src/components/recipient-select/recipient-select.test.tsx +139 -0
  24. package/src/components/recipient-select/recipient-select.tsx +94 -0
  25. package/src/index.ts +3 -0
@@ -0,0 +1,94 @@
1
+ import type { ContactAddress, SendRecipient } from '@proteos/sdk'
2
+ import { useEffect, useMemo, useRef, useState } from 'react'
3
+
4
+ import type { SearchRecipients } from '../message-composer/message-composer'
5
+ import { MultiSelect, type MultiSelectOption } from '../multi-select/multi-select'
6
+
7
+ /** Debounce for the injected directory search (`q`). */
8
+ const SEARCH_DEBOUNCE_MS = 250
9
+
10
+ /* =========================================================================
11
+ RecipientSelect — a searchable multi-select over the contact-address
12
+ directory a connection can reach, emitting SendRecipient[] (all
13
+ kind=contact-address). Backs the composer's To/Cc/Bcc rows. Typing filters
14
+ the loaded options locally AND (debounced) runs the injected
15
+ `searchRecipients` server-side, so parties beyond the first page are
16
+ findable; already-selected ids are always included so their chips render
17
+ even when off the current result page. Fetch-agnostic: the host injects
18
+ the search; stale responses are ignored and a failed search keeps the
19
+ previous options (best-effort).
20
+ ========================================================================= */
21
+
22
+ export interface RecipientSelectProps {
23
+ connectionId: string
24
+ searchRecipients: SearchRecipients
25
+ value: SendRecipient[]
26
+ onChange: (next: SendRecipient[]) => void
27
+ placeholder?: string
28
+ }
29
+
30
+ export function RecipientSelect({
31
+ connectionId,
32
+ searchRecipients,
33
+ value,
34
+ onChange,
35
+ placeholder,
36
+ }: RecipientSelectProps) {
37
+ const [search, setSearch] = useState('')
38
+ const [debouncedSearch, setDebouncedSearch] = useState('')
39
+ useEffect(() => {
40
+ const handle = window.setTimeout(() => setDebouncedSearch(search.trim()), SEARCH_DEBOUNCE_MS)
41
+ return () => window.clearTimeout(handle)
42
+ }, [search])
43
+
44
+ const [addresses, setAddresses] = useState<ContactAddress[]>([])
45
+ // Monotonic request counter — only the latest in-flight search may land.
46
+ const requestIdRef = useRef(0)
47
+ useEffect(() => {
48
+ if (connectionId === '') return
49
+ const requestId = ++requestIdRef.current
50
+ searchRecipients(connectionId, debouncedSearch)
51
+ .then((results) => {
52
+ if (requestId === requestIdRef.current) setAddresses(results)
53
+ })
54
+ .catch(() => {
55
+ // Best-effort directory: keep the previous options on failure.
56
+ })
57
+ }, [connectionId, debouncedSearch, searchRecipients])
58
+
59
+ const options = useMemo<MultiSelectOption<string>[]>(() => {
60
+ const map = new Map<string, MultiSelectOption<string>>()
61
+ for (const recipient of value) {
62
+ map.set(recipient.external_id, { id: recipient.external_id, label: recipient.external_id })
63
+ }
64
+ for (const address of addresses) {
65
+ const identifier = address.raw_value || address.value
66
+ map.set(identifier, {
67
+ id: identifier,
68
+ label: address.name || identifier,
69
+ })
70
+ }
71
+ return Array.from(map.values())
72
+ }, [addresses, value])
73
+
74
+ // The composer renders the row label (inline mono, like the Via/Subject
75
+ // rows) — none here.
76
+ return (
77
+ <MultiSelect
78
+ size="sm"
79
+ isSearchable
80
+ onSearchChange={setSearch}
81
+ {...(placeholder !== undefined ? { placeholder } : {})}
82
+ options={options}
83
+ selectedKeys={value.map((recipient) => recipient.external_id)}
84
+ onSelectionChange={(keys) =>
85
+ onChange(
86
+ Array.from(keys).map((id) => ({
87
+ kind: 'contact-address' as const,
88
+ external_id: String(id),
89
+ })),
90
+ )
91
+ }
92
+ />
93
+ )
94
+ }
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export * from './components/currency-input'
14
14
  export * from './components/data-table'
15
15
  export * from './components/date-picker'
16
16
  export * from './components/date-time-picker'
17
+ export * from './components/draft-message-composer'
17
18
  export * from './components/empty-state'
18
19
  export * from './components/error-animation'
19
20
  export * from './components/field'
@@ -24,12 +25,14 @@ export * from './components/image-lightbox'
24
25
  export * from './components/input'
25
26
  export * from './components/kbd'
26
27
  export * from './components/menu'
28
+ export * from './components/message-composer'
27
29
  export * from './components/modal'
28
30
  export * from './components/multi-select'
29
31
  export * from './components/nav'
30
32
  export * from './components/popover'
31
33
  export * from './components/progress-bar'
32
34
  export * from './components/radio-group'
35
+ export * from './components/recipient-select'
33
36
  export * from './components/record-filter'
34
37
  export * from './components/segmented-control'
35
38
  export * from './components/select'