@bootnodedev/canton-dappbooster 0.3.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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/connect.d.ts +135 -0
- package/dist/connect.js +296 -0
- package/dist/index.d.ts +704 -0
- package/dist/index.js +1675 -0
- package/dist/json-CVZFwEw1.js +29 -0
- package/dist/sumHoldings-BS0bHl7z.d.ts +94 -0
- package/package.json +74 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
import { a as Token, i as InstrumentId, n as InstrumentBalance, o as UseTokenListResult, r as sumHoldings, t as Holding } from "./sumHoldings-BS0bHl7z.js";
|
|
2
|
+
import { AnchorHTMLAttributes, FocusEventHandler, HTMLAttributes, InputHTMLAttributes, ReactElement, ReactNode } from "react";
|
|
3
|
+
//#region src/components/ExplorerLink/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Props for {@link ExplorerLink}. The label and the href are both required: the only content is an
|
|
6
|
+
* `aria-hidden` icon, and a link with nowhere to go is not rendered.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const href = explorerLink(partyId)
|
|
10
|
+
* href !== undefined && <ExplorerLink href={href} aria-label="View party id in explorer" />
|
|
11
|
+
*
|
|
12
|
+
* @category Components
|
|
13
|
+
*/
|
|
14
|
+
interface ExplorerLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "rel" | "target"> {
|
|
15
|
+
"aria-label": string;
|
|
16
|
+
href: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* An icon-only external link to a block explorer. Composes no URLs; pair it with `useExplorerLink`
|
|
20
|
+
* or `getExplorerLink`, which turn an identifier into an href.
|
|
21
|
+
*
|
|
22
|
+
* Reach for it directly when the link stands alone. Inside `Identifier` it is already the `href`
|
|
23
|
+
* slot, so pass the href there instead of nesting one.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* <ExplorerLink href="https://scan.example/party/nico" aria-label="View party id in explorer" />
|
|
27
|
+
*
|
|
28
|
+
* @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/ExplorerLink/anatomy.ts) for the part classes and state attributes the theme selects.
|
|
29
|
+
*
|
|
30
|
+
* @category Components
|
|
31
|
+
*/
|
|
32
|
+
declare const ExplorerLink: ({ className, href, ...rest }: ExplorerLinkProps) => ReactElement;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/components/Identifier/truncate.d.ts
|
|
35
|
+
/**
|
|
36
|
+
* Character counts overriding the display defaults of {@link truncateIdentifier}: how much to keep
|
|
37
|
+
* either side of the ellipsis, the segment length below which nothing is cut, and the bound on a
|
|
38
|
+
* party id's hint, which is otherwise kept whole however long it is.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* truncateIdentifier(partyId, { head: 4, tail: 4, threshold: 22 })
|
|
42
|
+
* truncateIdentifier(partyId, { hint: 12 })
|
|
43
|
+
*
|
|
44
|
+
* @category Utilities
|
|
45
|
+
*/
|
|
46
|
+
interface TruncateOptions {
|
|
47
|
+
head?: number;
|
|
48
|
+
tail?: number;
|
|
49
|
+
threshold?: number;
|
|
50
|
+
hint?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Truncates an identifier for display. A Canton party id is `hint::fingerprint`, so the hint
|
|
54
|
+
* survives whole and only the fingerprint shrinks, unless `hint` bounds it too. Anything else is
|
|
55
|
+
* middle-truncated as one segment, never longer than the input, and cut on UTF-16 code units, so a
|
|
56
|
+
* non-ASCII value can split a surrogate pair.
|
|
57
|
+
*
|
|
58
|
+
* Reach for this over `<Identifier>` inside a sentence or another `<button>`, where the copy
|
|
59
|
+
* control would nest a button in a button. Pass `hint` where the result must fit a bounded width.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* truncateIdentifier('nico::1220df946c5b01ad0f2d2b480f1f43b1d1f2e498f5a49c2f0b1cbb46')
|
|
63
|
+
* // 'nico::1220df…0b1cbb46'
|
|
64
|
+
* truncateIdentifier('treasury-operations::1220df94…', { hint: 12 })
|
|
65
|
+
* // 'treasury-ope…::1220df94…'
|
|
66
|
+
*
|
|
67
|
+
* @category Utilities
|
|
68
|
+
*/
|
|
69
|
+
declare const truncateIdentifier: (value: string, options?: TruncateOptions) => string;
|
|
70
|
+
/**
|
|
71
|
+
* The readable half of a party id. Returns the whole value when there is no separator. Reach for
|
|
72
|
+
* this over {@link truncateIdentifier} where the fingerprint should be dropped rather than
|
|
73
|
+
* shortened, such as a table column or a chart label.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* partyHint('nico::1220df94') // 'nico'
|
|
77
|
+
*
|
|
78
|
+
* @category Utilities
|
|
79
|
+
*/
|
|
80
|
+
declare const partyHint: (value: string) => string;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/hooks/useCopyToClipboard.d.ts
|
|
83
|
+
/**
|
|
84
|
+
* Transient result of the last copy, for styling an affordance. Returns to `idle` on a timer.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* <button data-state={state}>{state === 'copied' ? <CheckIcon /> : <CopyIcon />}</button>
|
|
88
|
+
*
|
|
89
|
+
* @category Hooks
|
|
90
|
+
*/
|
|
91
|
+
type CopyState = "idle" | "copied" | "error";
|
|
92
|
+
/**
|
|
93
|
+
* Result of one copy call. A rejected clipboard write is an outcome, not a thrown error.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* const outcome = await copy(partyId)
|
|
97
|
+
* if (!outcome.ok) toast.error(outcome.error.message)
|
|
98
|
+
*
|
|
99
|
+
* @category Hooks
|
|
100
|
+
*/
|
|
101
|
+
type CopyOutcome = {
|
|
102
|
+
ok: true;
|
|
103
|
+
value: string;
|
|
104
|
+
} | {
|
|
105
|
+
ok: false;
|
|
106
|
+
error: Error;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* How long {@link useCopyToClipboard} holds `copied`/`error` before returning to `idle`. Omitted
|
|
110
|
+
* fields fall back to a 1200 ms reset.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* useCopyToClipboard({ resetMs: 4000 }) // hold `copied` long enough to read a toast alongside it
|
|
114
|
+
*
|
|
115
|
+
* @category Hooks
|
|
116
|
+
*/
|
|
117
|
+
interface UseCopyToClipboardOptions {
|
|
118
|
+
resetMs?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Return shape of {@link useCopyToClipboard}, held by callers rendering their own copy control.
|
|
122
|
+
* `<Identifier>` consumes it internally.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* const clipboard: UseCopyToClipboardResult = useCopyToClipboard()
|
|
126
|
+
* <CopyButton {...clipboard} value={partyId} />
|
|
127
|
+
*
|
|
128
|
+
* @category Hooks
|
|
129
|
+
*/
|
|
130
|
+
interface UseCopyToClipboardResult {
|
|
131
|
+
state: CopyState;
|
|
132
|
+
copy: (value: string) => Promise<CopyOutcome>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Clipboard write with a transient result state. Callers that need their own feedback (a toast,
|
|
136
|
+
* say) use the returned outcome; callers that only need an affordance style off `state`.
|
|
137
|
+
*
|
|
138
|
+
* Reach for this over `<Identifier>` when the copy control must be a sibling of the value rather
|
|
139
|
+
* than a child of it.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const { state, copy } = useCopyToClipboard({ resetMs: 500 })
|
|
143
|
+
* <button onClick={() => void copy(partyId)} data-state={state}>Copy</button>
|
|
144
|
+
*
|
|
145
|
+
* @category Hooks
|
|
146
|
+
*/
|
|
147
|
+
declare const useCopyToClipboard: (options?: UseCopyToClipboardOptions) => UseCopyToClipboardResult;
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/components/Identifier/index.d.ts
|
|
150
|
+
/**
|
|
151
|
+
* Props for {@link Identifier}. `label` is the accessible noun the controls are named after, and
|
|
152
|
+
* `onCopy` is the clipboard outcome, not the DOM clipboard event.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* <Identifier value={partyId} label="party id" truncate={{ head: 4, tail: 4 }} />
|
|
156
|
+
*
|
|
157
|
+
* @category Components
|
|
158
|
+
*/
|
|
159
|
+
interface IdentifierProps extends Omit<HTMLAttributes<HTMLSpanElement>, "onCopy"> {
|
|
160
|
+
announce?: boolean;
|
|
161
|
+
copy?: boolean;
|
|
162
|
+
href?: string;
|
|
163
|
+
label?: string;
|
|
164
|
+
onCopy?: (outcome: CopyOutcome) => void;
|
|
165
|
+
truncate?: false | TruncateOptions;
|
|
166
|
+
value: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Displays a Canton identifier: truncated for reading, copyable in full, optionally linked to an
|
|
170
|
+
* explorer. Copy always writes the whole value, never the truncated display value. The href is
|
|
171
|
+
* built by the caller; this component composes no URLs. Renders a `span`, so it is legal anywhere
|
|
172
|
+
* inline text is.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* <Identifier value={partyId} label="party id" href={explorerLink(partyId)} announce={false} />
|
|
176
|
+
*
|
|
177
|
+
* @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/Identifier/anatomy.ts) for the part classes and state attributes the theme selects.
|
|
178
|
+
*
|
|
179
|
+
* @category Components
|
|
180
|
+
*/
|
|
181
|
+
declare const Identifier: ({ value, label, truncate, copy, announce, href, onCopy, className, ...rest }: IdentifierProps) => ReactElement;
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/utils/partyId.d.ts
|
|
184
|
+
/**
|
|
185
|
+
* Why a value is not a well-formed party id. Codes rather than sentences: L2 ships no user-facing
|
|
186
|
+
* copy, so the consumer maps these to their own wording.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* const MESSAGES: Record<PartyIdError, string> = {
|
|
190
|
+
* 'missing-separator': 'Use hint::fingerprint',
|
|
191
|
+
* 'invalid-hint': 'The hint cannot be blank or contain spaces',
|
|
192
|
+
* 'invalid-fingerprint': 'The fingerprint is 68 hex characters',
|
|
193
|
+
* }
|
|
194
|
+
*
|
|
195
|
+
* @category Utilities
|
|
196
|
+
*/
|
|
197
|
+
type PartyIdError = "missing-separator" | "invalid-hint" | "invalid-fingerprint";
|
|
198
|
+
/**
|
|
199
|
+
* Checks the shape of a party id: a non-blank hint, the `::` separator, and a 68-character hex
|
|
200
|
+
* fingerprint. Returns `undefined` when nothing is wrong. Shape only — whether the party exists is
|
|
201
|
+
* the ledger's answer, not this function's.
|
|
202
|
+
*
|
|
203
|
+
* Reach for this over {@link isValidPartyId} when the caller needs to say what went wrong.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* validatePartyId('nico:1220df94') // 'missing-separator'
|
|
207
|
+
* validatePartyId('nico::1220df94') // 'invalid-fingerprint': 8 hex characters, not 68
|
|
208
|
+
*
|
|
209
|
+
* @category Utilities
|
|
210
|
+
*/
|
|
211
|
+
declare const validatePartyId: (value: string) => PartyIdError | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Whether a party id is well-formed. Reach for {@link validatePartyId} instead where the reason
|
|
214
|
+
* matters.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* isValidPartyId(partyId) // true
|
|
218
|
+
* isValidPartyId('nico::1220df94') // false: 8 hex characters where 68 are required
|
|
219
|
+
*
|
|
220
|
+
* @category Utilities
|
|
221
|
+
*/
|
|
222
|
+
declare const isValidPartyId: (value: string) => boolean;
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/components/PartyIdInput/index.d.ts
|
|
225
|
+
/**
|
|
226
|
+
* Props for {@link PartyIdInput}. `onChange` reports the reason alongside the value, so the app can
|
|
227
|
+
* word the message and place it where it wants.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* <PartyIdInput
|
|
231
|
+
* value={receiver}
|
|
232
|
+
* onChange={(next, error) => { setReceiver(next); setError(error) }}
|
|
233
|
+
* />
|
|
234
|
+
*
|
|
235
|
+
* @category Components
|
|
236
|
+
*/
|
|
237
|
+
interface PartyIdInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "type" | "value"> {
|
|
238
|
+
onChange: (value: string, error: PartyIdError | undefined) => void;
|
|
239
|
+
value: string;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* A controlled text field for a Canton party id.
|
|
243
|
+
*
|
|
244
|
+
* It flags a malformed value with `aria-invalid` and hands the reason to `onChange`. Pass
|
|
245
|
+
* `aria-invalid` to flag the field for a reason the kit cannot know, such as a party the app
|
|
246
|
+
* rejects.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* <PartyIdInput value={receiver} onChange={setReceiver} aria-describedby="receiver-error" />
|
|
250
|
+
*
|
|
251
|
+
* @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/PartyIdInput/anatomy.ts) for the part classes and state attributes the theme selects.
|
|
252
|
+
*
|
|
253
|
+
* @category Components
|
|
254
|
+
*/
|
|
255
|
+
declare const PartyIdInput: ({ "aria-invalid": ariaInvalid, className, onBlur, onChange, value, ...rest }: PartyIdInputProps) => ReactElement;
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/utils/tokenAmount.d.ts
|
|
258
|
+
/**
|
|
259
|
+
* Decimal places Daml `Decimal` (`Numeric 10`) accepts. Pass a smaller one where a token's own
|
|
260
|
+
* precision is tighter.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* parseAmount('1.5', DEFAULT_PRECISION)
|
|
264
|
+
*
|
|
265
|
+
* @category Utilities
|
|
266
|
+
*/
|
|
267
|
+
declare const DEFAULT_PRECISION = 10;
|
|
268
|
+
/**
|
|
269
|
+
* Why an amount is not usable. Codes rather than sentences: L2 ships no user-facing copy, so the
|
|
270
|
+
* consumer maps these to their own wording.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* const MESSAGES: Record<TokenAmountError, string> = {
|
|
274
|
+
* 'not-a-number': 'Enter an amount',
|
|
275
|
+
* 'too-many-decimals': 'At most 10 decimal places',
|
|
276
|
+
* 'too-large': 'Larger than the ledger can hold',
|
|
277
|
+
* 'above-max': 'More than you hold',
|
|
278
|
+
* 'invalid-max': 'Balance unavailable',
|
|
279
|
+
* }
|
|
280
|
+
*
|
|
281
|
+
* @category Utilities
|
|
282
|
+
*/
|
|
283
|
+
type TokenAmountError = "not-a-number" | "too-many-decimals" | "too-large" | "above-max" | "invalid-max";
|
|
284
|
+
/**
|
|
285
|
+
* Scales a decimal string to an integer at `precision`, which is the only exact way to compare two
|
|
286
|
+
* amounts. `undefined` when the value is not a decimal or carries more places than `precision` can
|
|
287
|
+
* hold, since dropping a digit would silently change the amount.
|
|
288
|
+
*
|
|
289
|
+
* Reach for {@link validateAmount} instead where the caller needs to say what went wrong.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* parseAmount('1.5') // 15000000000n
|
|
293
|
+
*
|
|
294
|
+
* @category Utilities
|
|
295
|
+
*/
|
|
296
|
+
declare const parseAmount: (value: string, precision?: number) => bigint | undefined;
|
|
297
|
+
/**
|
|
298
|
+
* The inverse of {@link parseAmount}: a scaled integer back to a canonical decimal string with no
|
|
299
|
+
* trailing zeros.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* formatScaled(15000000000n) // '1.5'
|
|
303
|
+
*
|
|
304
|
+
* @category Utilities
|
|
305
|
+
*/
|
|
306
|
+
declare const formatScaled: (scaled: bigint, precision?: number) => string;
|
|
307
|
+
/**
|
|
308
|
+
* Groups the integer part for reading and leaves the fraction verbatim, so a value still being
|
|
309
|
+
* typed (`1.`, `1.50`) survives. `Intl` is handed the string unparsed, which formats it exactly
|
|
310
|
+
* where the float would drift. The grouping and decimal separators are the locale's, so
|
|
311
|
+
* {@link sanitizeAmountInput} has to read the result back under the same locale.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* formatAmount('8421337.1234567891') // '8,421,337.1234567891'
|
|
315
|
+
*
|
|
316
|
+
* @category Utilities
|
|
317
|
+
*/
|
|
318
|
+
declare const formatAmount: (value: string, locale?: string) => string;
|
|
319
|
+
/**
|
|
320
|
+
* Reduces raw field input to a decimal: grouping separators and anything that can never belong to
|
|
321
|
+
* an amount are dropped rather than flagged, so a paste of `1,234.5` lands as `1234.5`. A sign or
|
|
322
|
+
* an exponent is kept instead of stripped, so `-5` and `1.5e3` reach {@link validateAmount} as
|
|
323
|
+
* `not-a-number` rather than being salvaged into an amount nobody entered.
|
|
324
|
+
*
|
|
325
|
+
* The inverse of {@link formatAmount}, and it must be passed the same `locale`: under a
|
|
326
|
+
* comma-decimal locale a mismatched pair reads a value a thousand times too small.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* sanitizeAmountInput('.5') // '0.5'
|
|
330
|
+
*
|
|
331
|
+
* @category Utilities
|
|
332
|
+
*/
|
|
333
|
+
declare const sanitizeAmountInput: (input: string, locale?: string) => string;
|
|
334
|
+
/**
|
|
335
|
+
* Checks an amount against the token's precision, the `Numeric 38,10` ceiling, and an optional
|
|
336
|
+
* range. Returns `undefined` when nothing is wrong, including for the empty string: empty is empty,
|
|
337
|
+
* and required-ness belongs to the form. A `max` that is not itself a decimal returns `invalid-max`
|
|
338
|
+
* rather than reading as no ceiling, so a malformed balance cannot silently uncap the amount.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* validateAmount('1.5000000001', { max: '1.5' }) // 'above-max'
|
|
342
|
+
*
|
|
343
|
+
* @category Utilities
|
|
344
|
+
*/
|
|
345
|
+
declare const validateAmount: (value: string, { precision, max }?: {
|
|
346
|
+
precision?: number;
|
|
347
|
+
max?: string;
|
|
348
|
+
}) => TokenAmountError | undefined;
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/components/TokenInput/index.d.ts
|
|
351
|
+
/**
|
|
352
|
+
* The token an amount is denominated in: what the field renders, and no more. A `Token` off the
|
|
353
|
+
* list provider satisfies it, so a pick goes straight back into the field. `onTokenSelect` hands
|
|
354
|
+
* back the whole `Token`, identity included, because that is what the picker resolved.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* const CC: TokenMeta = { symbol: 'CC', logo: <CantonCoinIcon /> }
|
|
358
|
+
*
|
|
359
|
+
* @category Components
|
|
360
|
+
*/
|
|
361
|
+
interface TokenMeta {
|
|
362
|
+
symbol: string;
|
|
363
|
+
logo?: ReactNode;
|
|
364
|
+
}
|
|
365
|
+
/** @inline */
|
|
366
|
+
interface TokenInputOwnProps extends Omit<HTMLAttributes<HTMLDivElement>, "aria-errormessage" | "aria-required" | "autoCapitalize" | "autoCorrect" | "autoFocus" | "children" | "enterKeyHint" | "inputMode" | "onBlur" | "onChange" | "onFocus" | "spellCheck" | "tabIndex"> {
|
|
367
|
+
balance?: string;
|
|
368
|
+
balanceState?: "loading" | "error";
|
|
369
|
+
disabled?: boolean;
|
|
370
|
+
favoriteIds?: readonly InstrumentId[];
|
|
371
|
+
label?: string;
|
|
372
|
+
onBlur?: FocusEventHandler<HTMLInputElement>;
|
|
373
|
+
onChange: (value: string, error: TokenAmountError | undefined) => void;
|
|
374
|
+
onFocus?: FocusEventHandler<HTMLInputElement>;
|
|
375
|
+
onTokenSelect?: (token: Token) => void;
|
|
376
|
+
token: TokenMeta;
|
|
377
|
+
usdValue?: string;
|
|
378
|
+
value: string;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Props for {@link TokenInput}.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* <TokenInput label="Amount" token={{ symbol: 'CC' }} value={amount} balance={balance}
|
|
385
|
+
* usdValue="0.10" onChange={(next, error) => { setAmount(next); setError(error) }} />
|
|
386
|
+
*
|
|
387
|
+
* @category Components
|
|
388
|
+
*/
|
|
389
|
+
type TokenInputProps = TokenInputOwnProps & ({
|
|
390
|
+
label: string;
|
|
391
|
+
} | {
|
|
392
|
+
"aria-label": string;
|
|
393
|
+
} | {
|
|
394
|
+
"aria-labelledby": string;
|
|
395
|
+
});
|
|
396
|
+
/**
|
|
397
|
+
* A controlled field for a Canton token amount. The value stays a decimal string end to end, since
|
|
398
|
+
* a `number` cannot carry `Numeric 10` without losing digits; grouping separators are added for
|
|
399
|
+
* reading and stripped on the way back. Max fills from `balance`, which is also the ceiling
|
|
400
|
+
* `onChange` validates against, so the button never offers more than the field will accept.
|
|
401
|
+
* Passing `onTokenSelect` turns the symbol into a picker over the `TokenListProvider` list.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* <TokenInput label="Amount" token={{ symbol: 'CC' }} value={amount} balance={balance}
|
|
405
|
+
* onChange={(next, error) => { setAmount(next); setError(error) }} />
|
|
406
|
+
*
|
|
407
|
+
* @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/TokenInput/anatomy.ts) for the part classes and state attributes the theme selects.
|
|
408
|
+
*
|
|
409
|
+
* @category Components
|
|
410
|
+
*/
|
|
411
|
+
declare const TokenInput: ({ "aria-describedby": describedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, balance, balanceState, className, disabled, favoriteIds, id, label, onBlur, onChange, onFocus, onTokenSelect, usdValue, token, value, ...rest }: TokenInputProps) => ReactElement;
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/components/TokenInput/formatFigure.d.ts
|
|
414
|
+
/**
|
|
415
|
+
* Overrides for {@link formatFigure}: the decimal places to round to, 2 by default and clamped to
|
|
416
|
+
* what a ledger amount holds, and the locale whose grouping and decimal separators to use, the
|
|
417
|
+
* runtime's own by default.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* formatFigure('1234.5', { places: 4, locale: 'de-DE' }) // '1.234,5000'
|
|
421
|
+
*
|
|
422
|
+
* @category Utilities
|
|
423
|
+
*/
|
|
424
|
+
interface FormatFigureOptions {
|
|
425
|
+
locale?: string;
|
|
426
|
+
places?: number;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Rounds an amount to a fixed number of decimals and groups it for reading, so a column of figures
|
|
430
|
+
* compares at a glance where {@link formatAmount} keeps every digit the ledger carries. An amount
|
|
431
|
+
* it cannot read formats to nothing rather than to a zero that would read as a real balance.
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* formatFigure('1234.5') // '1,234.50'
|
|
435
|
+
* formatFigure('abc') // undefined
|
|
436
|
+
*
|
|
437
|
+
* @category Utilities
|
|
438
|
+
*/
|
|
439
|
+
declare const formatFigure: (value: string | undefined, { locale, places }?: FormatFigureOptions) => string | undefined;
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region src/hooks/useExplorerLink.d.ts
|
|
442
|
+
/**
|
|
443
|
+
* What an identifier points at. `update` is the ledger transaction, which scans label as one.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* getExplorerLink({ explorer, value: contractId, entity: 'contract' })
|
|
447
|
+
*
|
|
448
|
+
* @category Utilities
|
|
449
|
+
*/
|
|
450
|
+
type ExplorerEntity = "party" | "contract" | "update";
|
|
451
|
+
/**
|
|
452
|
+
* Canton has no chain registry and no canonical explorer: scans are per-environment and per-SV,
|
|
453
|
+
* so the app resolves the base url once from its own config and hands it over.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* const explorer: ExplorerConfig = { baseUrl: 'https://scan.example' }
|
|
457
|
+
*
|
|
458
|
+
* @category Utilities
|
|
459
|
+
*/
|
|
460
|
+
interface ExplorerConfig {
|
|
461
|
+
baseUrl: string;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Arguments for {@link getExplorerLink}.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* getExplorerLink({ explorer, value: partyId })
|
|
468
|
+
*
|
|
469
|
+
* @category Utilities
|
|
470
|
+
*/
|
|
471
|
+
interface GetExplorerLinkParams {
|
|
472
|
+
explorer: ExplorerConfig;
|
|
473
|
+
value: string;
|
|
474
|
+
entity?: ExplorerEntity;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Builds an explorer URL for a Canton identifier. Returns `undefined` whenever no link can be made.
|
|
478
|
+
*
|
|
479
|
+
* @throws when `explorer.baseUrl` is empty or blank, which is a misconfigured app rather than a
|
|
480
|
+
* missing link.
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* getExplorerLink({ explorer, value: partyId })
|
|
484
|
+
* // 'https://scan.example/party/…', the party shape having matched on its own
|
|
485
|
+
* getExplorerLink({ explorer, value: '1220df94a1', entity: 'update' })
|
|
486
|
+
* // 'https://scan.example/update/1220df94a1', the entity overriding a shape that matched nothing
|
|
487
|
+
* getExplorerLink({ explorer, value: 'nico' })
|
|
488
|
+
* // undefined: no shape matched, and no entity said otherwise
|
|
489
|
+
*
|
|
490
|
+
* @category Utilities
|
|
491
|
+
*/
|
|
492
|
+
declare const getExplorerLink: ({ explorer, value, entity }: GetExplorerLinkParams) => string | undefined;
|
|
493
|
+
/**
|
|
494
|
+
* Holds an explorer config so call sites pass only an identifier, and returns
|
|
495
|
+
* {@link getExplorerLink} bound to it.
|
|
496
|
+
*
|
|
497
|
+
* @throws on render when `explorer.baseUrl` is empty or blank, as {@link getExplorerLink} does.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* const explorerLink = useExplorerLink({ baseUrl: 'https://scan.example' })
|
|
501
|
+
* <Identifier value={partyId} href={explorerLink(partyId)} />
|
|
502
|
+
* <Identifier value={cid} href={explorerLink(cid, 'contract')} />
|
|
503
|
+
* // no href at all where the value matched no shape and no entity said otherwise
|
|
504
|
+
*
|
|
505
|
+
* @category Hooks
|
|
506
|
+
*/
|
|
507
|
+
declare const useExplorerLink: (explorer: ExplorerConfig) => ((value: string, entity?: ExplorerEntity) => string | undefined);
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/providers/ThemeProvider/index.d.ts
|
|
510
|
+
/**
|
|
511
|
+
* Props for {@link ThemeProvider}. `storageKey` isolates two apps sharing an origin; it is read on
|
|
512
|
+
* mount, so changing it later moves where writes go without re-reading the new key.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
* <ThemeProvider storageKey="vesting-theme">{children}</ThemeProvider>
|
|
516
|
+
*
|
|
517
|
+
* @category Components
|
|
518
|
+
*/
|
|
519
|
+
interface ThemeProviderProps {
|
|
520
|
+
children: ReactNode;
|
|
521
|
+
storageKey?: string;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Owns the light / dark / system choice: persists it, follows the OS while on `system`, tracks the
|
|
525
|
+
* key across tabs, and writes the resolved value to `data-theme` on `<html>`, which is what
|
|
526
|
+
* `@bootnodedev/canton-theme` keys its dark values on. Renders no DOM of its own.
|
|
527
|
+
*
|
|
528
|
+
* The attribute lands before the tree below it paints, but not before the page background; that
|
|
529
|
+
* flash is accepted, and `architecture.md` has the reasoning. Client-only, because it reads the OS
|
|
530
|
+
* preference while picking its initial state, so a server render throws.
|
|
531
|
+
*
|
|
532
|
+
* @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/providers/ThemeProvider/anatomy.ts) for the state attribute the theme selects.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* createRoot(el).render(
|
|
536
|
+
* <ThemeProvider>
|
|
537
|
+
* <App />
|
|
538
|
+
* </ThemeProvider>,
|
|
539
|
+
* )
|
|
540
|
+
*
|
|
541
|
+
* @category Components
|
|
542
|
+
*/
|
|
543
|
+
declare const ThemeProvider: ({ children, storageKey }: ThemeProviderProps) => ReactElement;
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/providers/ThemeProvider/context.d.ts
|
|
546
|
+
/**
|
|
547
|
+
* What the user picked. `system` defers to the OS and keeps following it.
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* setMode('system') // keeps following the OS afterwards, unlike setMode(resolved)
|
|
551
|
+
*
|
|
552
|
+
* @category Hooks
|
|
553
|
+
*/
|
|
554
|
+
type ThemeMode = "light" | "dark" | "system";
|
|
555
|
+
/**
|
|
556
|
+
* What is actually on the document: `system` resolved against the OS preference.
|
|
557
|
+
*
|
|
558
|
+
* @example
|
|
559
|
+
* const sheet: Record<ResolvedTheme, string> = { light: lightSheet, dark: darkSheet }
|
|
560
|
+
*
|
|
561
|
+
* @category Hooks
|
|
562
|
+
*/
|
|
563
|
+
type ResolvedTheme = "light" | "dark";
|
|
564
|
+
/**
|
|
565
|
+
* Return shape of {@link useTheme}. `toggle` is a two-way switch: it leaves `system` for the
|
|
566
|
+
* opposite of what is showing.
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* const { mode, resolved } = useTheme()
|
|
570
|
+
* mode === 'system' ? `Auto (${resolved})` : mode // 'Auto (dark)' vs 'light'
|
|
571
|
+
*
|
|
572
|
+
* @category Hooks
|
|
573
|
+
*/
|
|
574
|
+
interface UseThemeResult {
|
|
575
|
+
mode: ThemeMode;
|
|
576
|
+
resolved: ResolvedTheme;
|
|
577
|
+
setMode: (mode: ThemeMode) => void;
|
|
578
|
+
toggle: () => void;
|
|
579
|
+
}
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/providers/ThemeProvider/useTheme.d.ts
|
|
582
|
+
/**
|
|
583
|
+
* Reads and sets the theme mode.
|
|
584
|
+
*
|
|
585
|
+
* @throws with no {@link ThemeProvider} above it. There is no ambient fallback, because a control
|
|
586
|
+
* that silently fails to switch is worse than one that throws in dev.
|
|
587
|
+
*
|
|
588
|
+
* @example
|
|
589
|
+
* const { resolved, toggle } = useTheme()
|
|
590
|
+
* <button onClick={toggle}>{resolved === 'dark' ? 'Light' : 'Dark'} mode</button>
|
|
591
|
+
*
|
|
592
|
+
* @category Hooks
|
|
593
|
+
*/
|
|
594
|
+
declare const useTheme: () => UseThemeResult;
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/providers/TokenListProvider/index.d.ts
|
|
597
|
+
/**
|
|
598
|
+
* Props for {@link TokenListProvider}. `tokens` is read by identity, so hoist the array or memoise
|
|
599
|
+
* it; a fresh one on every render rebuilds the lookup map.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* <TokenListProvider tokens={mockTokens}>{children}</TokenListProvider>
|
|
603
|
+
*
|
|
604
|
+
* @category Components
|
|
605
|
+
*/
|
|
606
|
+
interface TokenListProviderProps {
|
|
607
|
+
children: ReactNode;
|
|
608
|
+
tokens: readonly Token[];
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Supplies the token list every picker in the tree chooses from.
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* <TokenListProvider tokens={tokens}>
|
|
615
|
+
* <TokenInput label="Amount" token={selected} value={amount} onChange={setAmount}
|
|
616
|
+
* onTokenSelect={setSelected} />
|
|
617
|
+
* </TokenListProvider>
|
|
618
|
+
*
|
|
619
|
+
* @category Components
|
|
620
|
+
*/
|
|
621
|
+
declare const TokenListProvider: ({ children, tokens }: TokenListProviderProps) => ReactElement;
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/providers/TokenListProvider/useTokenList.d.ts
|
|
624
|
+
/**
|
|
625
|
+
* Reads the token list a {@link TokenListProvider} supplies. Reach for it where a screen renders
|
|
626
|
+
* its own token UI; `<TokenInput onTokenSelect>` already reads it itself.
|
|
627
|
+
*
|
|
628
|
+
* @throws with no {@link TokenListProvider} above it.
|
|
629
|
+
*
|
|
630
|
+
* @example
|
|
631
|
+
* const { tokens } = useTokenList()
|
|
632
|
+
* tokens.map((token) => <TokenRow key={tokenKey(token.instrumentId)} token={token} />)
|
|
633
|
+
*
|
|
634
|
+
* @category Hooks
|
|
635
|
+
*/
|
|
636
|
+
declare const useTokenList: () => UseTokenListResult;
|
|
637
|
+
//#endregion
|
|
638
|
+
//#region src/utils/mergeTokens.d.ts
|
|
639
|
+
/**
|
|
640
|
+
* What one source knows about a token: its instrument id, and whichever fields it can fill.
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* const fromRegistry: PartialToken[] = [{ instrumentId, name: 'Canton Coin', symbol: 'CC' }]
|
|
644
|
+
*
|
|
645
|
+
* @category Utilities
|
|
646
|
+
*/
|
|
647
|
+
type PartialToken = Partial<Omit<Token, "instrumentId">> & {
|
|
648
|
+
instrumentId: InstrumentId;
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* Builds one row per instrument out of every source that knows about it, later sources winning
|
|
652
|
+
* field by field. A row is a token the picker can offer, so the union is the catalogue and a
|
|
653
|
+
* holdings source only annotates it: a token nobody holds is still a row, and one nothing named is
|
|
654
|
+
* a row under its own id.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* mergeTokens([[{ instrumentId, symbol: 'CC' }], sumHoldings(holdings)])
|
|
658
|
+
*
|
|
659
|
+
* @category Utilities
|
|
660
|
+
*/
|
|
661
|
+
declare const mergeTokens: (sources: readonly (readonly PartialToken[])[]) => readonly Token[];
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/utils/readInstruments.d.ts
|
|
664
|
+
/**
|
|
665
|
+
* What a registry says about one instrument it administers. No logo: the metadata API serves none,
|
|
666
|
+
* so artwork comes from the app or from a curated list.
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* const [{ name, symbol }] = await readInstruments(registryUrl)
|
|
670
|
+
*
|
|
671
|
+
* @category Utilities
|
|
672
|
+
*/
|
|
673
|
+
interface Instrument {
|
|
674
|
+
decimals: number;
|
|
675
|
+
instrumentId: InstrumentId;
|
|
676
|
+
name: string;
|
|
677
|
+
symbol: string;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Reads a registry's instrument metadata, following its pages up to a limit of 100, so the answer
|
|
681
|
+
* is the registry's whole catalogue and a registry that will not stop paging cannot hang the caller.
|
|
682
|
+
*
|
|
683
|
+
* @throws where either request answers anything but 200, or the reply is not JSON.
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* const instruments = await readInstruments('https://registry.example/api')
|
|
687
|
+
*
|
|
688
|
+
* @category Utilities
|
|
689
|
+
*/
|
|
690
|
+
declare const readInstruments: (registryUrl: string) => Promise<readonly Instrument[]>;
|
|
691
|
+
//#endregion
|
|
692
|
+
//#region src/utils/tokenKey.d.ts
|
|
693
|
+
/**
|
|
694
|
+
* The string identity of an instrument, for a map key, a React key or an equality check. Compare
|
|
695
|
+
* these rather than the `id` alone: two registries can both issue a `USDC`.
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* tokenKey({ admin: 'DSO::1220ab', id: 'Amulet' }) // 'DSO::1220ab/Amulet'
|
|
699
|
+
*
|
|
700
|
+
* @category Utilities
|
|
701
|
+
*/
|
|
702
|
+
declare const tokenKey: ({ admin, id }: InstrumentId) => string;
|
|
703
|
+
//#endregion
|
|
704
|
+
export { type CopyOutcome, type CopyState, DEFAULT_PRECISION, type ExplorerConfig, type ExplorerEntity, ExplorerLink, type ExplorerLinkProps, type FormatFigureOptions, type GetExplorerLinkParams, type Holding, Identifier, type IdentifierProps, type Instrument, type InstrumentBalance, type InstrumentId, type PartialToken, type PartyIdError, PartyIdInput, type PartyIdInputProps, type ResolvedTheme, type ThemeMode, ThemeProvider, type ThemeProviderProps, type Token, type TokenAmountError, TokenInput, type TokenInputProps, TokenListProvider, type TokenListProviderProps, type TokenMeta, type TruncateOptions, type UseCopyToClipboardOptions, type UseCopyToClipboardResult, type UseThemeResult, type UseTokenListResult, formatAmount, formatFigure, formatScaled, getExplorerLink, isValidPartyId, mergeTokens, parseAmount, partyHint, readInstruments, sanitizeAmountInput, sumHoldings, tokenKey, truncateIdentifier, useCopyToClipboard, useExplorerLink, useTheme, useTokenList, validateAmount, validatePartyId };
|