@mediansh/widget 0.6.0 → 0.7.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 +52 -3
- package/dist/index.d.ts +347 -16
- package/dist/index.js +2674 -1206
- package/dist/index.js.map +1 -1
- package/dist/widget.css +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@ support panel. An AI agent answers first, your team picks up in the Median
|
|
|
5
5
|
inbox, and replies stream back to the visitor. There is no connection or
|
|
6
6
|
message list to manage.
|
|
7
7
|
|
|
8
|
+
The same panel as a modal, a contact form, and a feedback panel ship
|
|
9
|
+
alongside it. Same key, same inbox.
|
|
10
|
+
|
|
8
11
|
## Install
|
|
9
12
|
|
|
10
13
|
```sh
|
|
@@ -66,6 +69,51 @@ medianSupport.reset(); // on sign out
|
|
|
66
69
|
React components can use `useMedianSupport()` for the same control with a
|
|
67
70
|
live `isOpen`.
|
|
68
71
|
|
|
72
|
+
## Modal
|
|
73
|
+
|
|
74
|
+
The widget, opened in the middle of the screen over a scrim rather than out
|
|
75
|
+
of the corner. The button stays; every prop and every control is the same.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { MedianSupportModal } from "@mediansh/widget";
|
|
79
|
+
|
|
80
|
+
<MedianSupportModal apiKey="median_pk_..." />;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`mountMedianSupportModal` is the version for pages without React. Mount one
|
|
84
|
+
of the two, not both.
|
|
85
|
+
|
|
86
|
+
## Contact form
|
|
87
|
+
|
|
88
|
+
A form where you put it, for the page where a chat panel is the wrong shape.
|
|
89
|
+
An email and a message are always asked; add the rest:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { MedianContactForm } from "@mediansh/widget";
|
|
93
|
+
|
|
94
|
+
<MedianContactForm
|
|
95
|
+
apiKey="median_pk_..."
|
|
96
|
+
fields={[
|
|
97
|
+
{ name: "name", label: "Name", required: true },
|
|
98
|
+
{ name: "topic", type: "select", options: ["Billing", "A bug"] },
|
|
99
|
+
]}
|
|
100
|
+
/>;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Sending starts a conversation in your inbox, exactly as the widget does, and
|
|
104
|
+
the reply goes to the email given. `mountMedianContactForm({ target, ... })`
|
|
105
|
+
renders it into an element of yours on a page without React.
|
|
106
|
+
|
|
107
|
+
Or keep your own form and only borrow the send:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const contact = useMedianContact({ apiKey: "median_pk_..." });
|
|
111
|
+
|
|
112
|
+
await contact.send({ email, message, fields: { Topic: topic } });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`sendMedianContact(options, message)` is the same call as a plain function.
|
|
116
|
+
|
|
69
117
|
## Feedback
|
|
70
118
|
|
|
71
119
|
A second panel, for notes rather than questions. It wraps a trigger of your
|
|
@@ -88,11 +136,12 @@ and the page it was written on rides along.
|
|
|
88
136
|
|
|
89
137
|
## Theming
|
|
90
138
|
|
|
91
|
-
|
|
92
|
-
a plain light theme without them. They inherit your font.
|
|
139
|
+
Every piece reads shadcn-style CSS variables from your page and falls back
|
|
140
|
+
to a plain light theme without them. They inherit your font.
|
|
93
141
|
|
|
94
142
|
## Docs
|
|
95
143
|
|
|
96
|
-
Props, identity, diagnostics, feedback, and
|
|
144
|
+
Props, identity, diagnostics, the modal, the contact form, feedback, and
|
|
145
|
+
agent tools:
|
|
97
146
|
[docs.median.sh](https://docs.median.sh), or the source at
|
|
98
147
|
[github.com/mediansh/median](https://github.com/mediansh/median).
|
package/dist/index.d.ts
CHANGED
|
@@ -260,19 +260,26 @@ type MedianSupportProps = MedianSupportOptions & ({
|
|
|
260
260
|
declare function MedianSupport(props: MedianSupportProps): react.JSX.Element;
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
|
-
* What a mounted
|
|
264
|
-
* no re-render to do for them. Everything else goes through
|
|
265
|
-
* same as anywhere.
|
|
263
|
+
* What a mounted component hands back: the two things a host without React
|
|
264
|
+
* has no re-render to do for them. Everything else goes through the
|
|
265
|
+
* component's own control, same as anywhere.
|
|
266
266
|
*/
|
|
267
|
-
type
|
|
267
|
+
type MedianMountInstance<Options> = {
|
|
268
268
|
/**
|
|
269
269
|
* Change any option after mount. Only the keys passed change, so
|
|
270
270
|
* `update({ user })` when somebody signs in leaves the rest alone.
|
|
271
271
|
*/
|
|
272
|
-
update: (options: Partial<
|
|
273
|
-
/** Take
|
|
272
|
+
update: (options: Partial<Options>) => void;
|
|
273
|
+
/** Take it off the page: the component, its container, its connection. */
|
|
274
274
|
unmount: () => void;
|
|
275
275
|
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* What a mounted widget hands back: the two things a host without React has
|
|
279
|
+
* no re-render to do for them. Everything else goes through `medianSupport`,
|
|
280
|
+
* same as anywhere.
|
|
281
|
+
*/
|
|
282
|
+
type MedianSupportInstance = MedianMountInstance<MedianSupportProps>;
|
|
276
283
|
/**
|
|
277
284
|
* The same widget, for a page that has never heard of React: Vue, Svelte,
|
|
278
285
|
* Rails, a script on plain HTML. One call puts the launcher in the corner,
|
|
@@ -293,6 +300,43 @@ type MedianSupportInstance = {
|
|
|
293
300
|
*/
|
|
294
301
|
declare function mountMedianSupport(options: MedianSupportProps): MedianSupportInstance;
|
|
295
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Every prop the widget takes, unchanged. The modal is the widget in a
|
|
305
|
+
* different place, not a different widget.
|
|
306
|
+
*/
|
|
307
|
+
type MedianSupportModalProps = MedianSupportProps;
|
|
308
|
+
/**
|
|
309
|
+
* The support widget as a modal: the same button in the corner, and a panel
|
|
310
|
+
* that opens in the middle of the screen over a scrim rather than out of
|
|
311
|
+
* the corner.
|
|
312
|
+
*
|
|
313
|
+
* <MedianSupportModal apiKey="median_pk_..." />
|
|
314
|
+
*
|
|
315
|
+
* Everything else is the widget's. Same props, same thread, same inbox on
|
|
316
|
+
* the other side, same `useMedianSupport()` to open it from your own nav,
|
|
317
|
+
* same sheet on a phone. Pick this for a site where a conversation deserves
|
|
318
|
+
* the room, or where a panel floating over the page would be in the way of
|
|
319
|
+
* the page.
|
|
320
|
+
*
|
|
321
|
+
* One or the other. Mounting both shares one open state, and the console
|
|
322
|
+
* says so.
|
|
323
|
+
*/
|
|
324
|
+
declare function MedianSupportModal(props: MedianSupportModalProps): react.JSX.Element;
|
|
325
|
+
|
|
326
|
+
type MedianSupportModalInstance = MedianMountInstance<MedianSupportModalProps>;
|
|
327
|
+
/**
|
|
328
|
+
* The modal, for a page that has never heard of React: Vue, Svelte, Rails,
|
|
329
|
+
* a script on plain HTML.
|
|
330
|
+
*
|
|
331
|
+
* const support = mountMedianSupportModal({ apiKey: "median_pk_..." });
|
|
332
|
+
*
|
|
333
|
+
* It takes the component's props as one options object, and the rest of the
|
|
334
|
+
* SDK does not know the difference: `medianSupport.open()` opens this modal
|
|
335
|
+
* exactly as it opens the component. `update` and `unmount` are the same two
|
|
336
|
+
* `mountMedianSupport` hands back.
|
|
337
|
+
*/
|
|
338
|
+
declare function mountMedianSupportModal(options: MedianSupportModalProps): MedianSupportModalInstance;
|
|
339
|
+
|
|
296
340
|
/**
|
|
297
341
|
* Filing the crash the visitor is looking at.
|
|
298
342
|
*
|
|
@@ -537,15 +581,7 @@ declare function MedianFeedback(props: MedianFeedbackProps): react.JSX.Element;
|
|
|
537
581
|
* no re-render to do for them. Opening and closing go through
|
|
538
582
|
* `medianFeedback`, same as anywhere.
|
|
539
583
|
*/
|
|
540
|
-
type MedianFeedbackInstance =
|
|
541
|
-
/**
|
|
542
|
-
* Change any option after mount. Only the keys passed change, so
|
|
543
|
-
* `update({ user })` when somebody signs in leaves the rest alone.
|
|
544
|
-
*/
|
|
545
|
-
update: (options: Partial<MedianFeedbackProps>) => void;
|
|
546
|
-
/** Take the panel off the page. */
|
|
547
|
-
unmount: () => void;
|
|
548
|
-
};
|
|
584
|
+
type MedianFeedbackInstance = MedianMountInstance<MedianFeedbackProps>;
|
|
549
585
|
/**
|
|
550
586
|
* The same panel, for a page that has never heard of React: Vue, Svelte,
|
|
551
587
|
* Rails, a script on plain HTML.
|
|
@@ -587,4 +623,299 @@ type MedianFeedbackControl = {
|
|
|
587
623
|
*/
|
|
588
624
|
declare function useMedianFeedback(): MedianFeedbackControl;
|
|
589
625
|
|
|
590
|
-
|
|
626
|
+
/**
|
|
627
|
+
* What a host can put on the contact form, and what the form always has.
|
|
628
|
+
*
|
|
629
|
+
* Two fields are on every form whatever the host says: an email, because a
|
|
630
|
+
* message nobody can reply to is a message nobody sent, and the message
|
|
631
|
+
* itself. Everything else is the host's to add: a name, a topic, an order
|
|
632
|
+
* number, whatever their team wants to know before they read.
|
|
633
|
+
*/
|
|
634
|
+
/** A line, a paragraph, or one of a list. */
|
|
635
|
+
type MedianContactFieldKind = "text" | "textarea" | "select";
|
|
636
|
+
/**
|
|
637
|
+
* One field a host adds to the form.
|
|
638
|
+
*
|
|
639
|
+
* { name: "topic", label: "Topic", type: "select", options: ["Billing", "A bug"] }
|
|
640
|
+
*
|
|
641
|
+
* Three names are reserved. `email` and `message` are the two the form always
|
|
642
|
+
* has, and naming either here only changes its label and placeholder. `name`
|
|
643
|
+
* lands on the customer record beside the email rather than in the message,
|
|
644
|
+
* so the team sees who wrote rather than a line saying so.
|
|
645
|
+
*/
|
|
646
|
+
type MedianContactField = {
|
|
647
|
+
/** Its key. Rides the message as the label on its line. */
|
|
648
|
+
name: string;
|
|
649
|
+
/** Shown above the box. Made from `name` when left out. */
|
|
650
|
+
label?: string;
|
|
651
|
+
/** `text` by default. Ignored on `email` and `message`, which know their shape. */
|
|
652
|
+
type?: MedianContactFieldKind;
|
|
653
|
+
/** What a `select` offers. A select with none falls back to a text box. */
|
|
654
|
+
options?: string[];
|
|
655
|
+
placeholder?: string;
|
|
656
|
+
/** Off by default. `email` and `message` are required whatever this says. */
|
|
657
|
+
required?: boolean;
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
type MedianContactFormOptions = {
|
|
661
|
+
/**
|
|
662
|
+
* What the form asks, beyond an email and a message, which it always asks:
|
|
663
|
+
*
|
|
664
|
+
* <MedianContactForm
|
|
665
|
+
* apiKey="median_pk_..."
|
|
666
|
+
* fields={[
|
|
667
|
+
* { name: "name", label: "Name", required: true },
|
|
668
|
+
* { name: "topic", type: "select", options: ["Billing", "A bug", "Something else"] },
|
|
669
|
+
* ]}
|
|
670
|
+
* />
|
|
671
|
+
*
|
|
672
|
+
* Shown in this order. Email goes first, or right after a `name` field,
|
|
673
|
+
* and the message goes last, unless either is placed here by name. A
|
|
674
|
+
* `name` field lands on the customer record; every other answer goes
|
|
675
|
+
* above the message as a line of its own.
|
|
676
|
+
*/
|
|
677
|
+
fields?: MedianContactField[];
|
|
678
|
+
/**
|
|
679
|
+
* Who is signed in on your site, if anyone. Their email and name fill the
|
|
680
|
+
* boxes in, and the rest is shown to your team.
|
|
681
|
+
*/
|
|
682
|
+
user?: MedianUser;
|
|
683
|
+
/**
|
|
684
|
+
* Whether the message carries what the browser already knows: time zone,
|
|
685
|
+
* language, browser, OS, device, screen, and the page it was sent from.
|
|
686
|
+
* On by default. Pass `false` and none of it is read at all.
|
|
687
|
+
*/
|
|
688
|
+
telemetry?: boolean;
|
|
689
|
+
/**
|
|
690
|
+
* Watch what goes wrong on the page, so a bug report filed from the
|
|
691
|
+
* message arrives with the error already attached. The same switch
|
|
692
|
+
* `<MedianSupport>` takes, and the same buffer behind it.
|
|
693
|
+
*/
|
|
694
|
+
diagnostics?: MedianDiagnostics;
|
|
695
|
+
/**
|
|
696
|
+
* Anything about your own app worth having next to a bug report: a build,
|
|
697
|
+
* a route, a flag. Read when the form is sent.
|
|
698
|
+
*/
|
|
699
|
+
appState?: Record<string, string | number | boolean>;
|
|
700
|
+
/** The heading, for a page that calls this something else. */
|
|
701
|
+
title?: string;
|
|
702
|
+
/** The line under it. */
|
|
703
|
+
description?: string;
|
|
704
|
+
/**
|
|
705
|
+
* The message landed. For whatever your page does next: a thank-you of its
|
|
706
|
+
* own, a redirect, an event. The form has already said thank you itself.
|
|
707
|
+
*/
|
|
708
|
+
onSent?: () => void;
|
|
709
|
+
};
|
|
710
|
+
/**
|
|
711
|
+
* Two ways in, the same two `<MedianSupport>` takes.
|
|
712
|
+
*
|
|
713
|
+
* `apiKey` is the publishable id on its own. `identity` is the path to a
|
|
714
|
+
* route built with `medianIdentity`, which answers with the key and with
|
|
715
|
+
* whoever is signed in, already signed. Pass both and the form is up on the
|
|
716
|
+
* key from the prop while the route is still answering.
|
|
717
|
+
*/
|
|
718
|
+
type MedianContactFormProps = MedianContactFormOptions & ({
|
|
719
|
+
apiKey: string;
|
|
720
|
+
identity?: string;
|
|
721
|
+
} | {
|
|
722
|
+
apiKey?: string;
|
|
723
|
+
identity: string;
|
|
724
|
+
});
|
|
725
|
+
/**
|
|
726
|
+
* A contact form, for the page where a chat panel is the wrong shape.
|
|
727
|
+
*
|
|
728
|
+
* <MedianContactForm apiKey="median_pk_..." />
|
|
729
|
+
*
|
|
730
|
+
* An email and a message, on a card the shape of the support panel, with
|
|
731
|
+
* whatever else your team wants asked first. What is sent starts a
|
|
732
|
+
* conversation in your inbox exactly as the widget would: the agent answers
|
|
733
|
+
* first, your team picks it up, and the reply goes to the email given.
|
|
734
|
+
*
|
|
735
|
+
* Built on `useMedianContact`, which is also what a form of your own uses.
|
|
736
|
+
* It needs no connection of its own, and it is independent of
|
|
737
|
+
* `<MedianSupport>`: mount either, both, or neither. Mounted together they
|
|
738
|
+
* share a visitor, so somebody who writes here and later opens the widget
|
|
739
|
+
* finds the same thread.
|
|
740
|
+
*/
|
|
741
|
+
declare function MedianContactForm(props: MedianContactFormProps): react.JSX.Element;
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* The component's props, plus where on the page the form goes.
|
|
745
|
+
*
|
|
746
|
+
* Spelled out rather than `Omit`ped off the props, because omitting a key
|
|
747
|
+
* from a union of two shapes flattens it into one and loses the "either,
|
|
748
|
+
* but not neither" the union is there to say.
|
|
749
|
+
*/
|
|
750
|
+
type MedianContactFormMountOptions = MedianContactFormProps & {
|
|
751
|
+
/**
|
|
752
|
+
* The element the form is rendered into, or a selector for it:
|
|
753
|
+
*
|
|
754
|
+
* mountMedianContactForm({ target: "#contact", apiKey: "median_pk_..." });
|
|
755
|
+
*
|
|
756
|
+
* Unlike the widget, a form has no corner to go to, so this is required.
|
|
757
|
+
*/
|
|
758
|
+
target: string | Element;
|
|
759
|
+
};
|
|
760
|
+
type MedianContactFormInstance = MedianMountInstance<MedianContactFormProps>;
|
|
761
|
+
/**
|
|
762
|
+
* The same form, for a page that has never heard of React: Vue, Svelte,
|
|
763
|
+
* Rails, a script on plain HTML.
|
|
764
|
+
*
|
|
765
|
+
* const form = mountMedianContactForm({
|
|
766
|
+
* target: "#contact",
|
|
767
|
+
* apiKey: "median_pk_...",
|
|
768
|
+
* fields: [{ name: "name", label: "Name" }],
|
|
769
|
+
* });
|
|
770
|
+
*
|
|
771
|
+
* It takes the component's props as one options object, plus `target`, and
|
|
772
|
+
* renders into a container of its own inside that element with the
|
|
773
|
+
* package's copy of React. Whatever else the element holds is left alone.
|
|
774
|
+
*/
|
|
775
|
+
declare function mountMedianContactForm({ target, ...options }: MedianContactFormMountOptions): MedianContactFormInstance;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* What a contact form hands over, ours or a host's own.
|
|
779
|
+
*
|
|
780
|
+
* The shape is deliberately small: the two things a message needs to be
|
|
781
|
+
* answered, who wrote it if they said, and whatever else the form asked as
|
|
782
|
+
* label to answer. Building one from a `<form>` of your own is a few lines.
|
|
783
|
+
*/
|
|
784
|
+
type MedianContactMessage = {
|
|
785
|
+
/** Where the reply goes. */
|
|
786
|
+
email: string;
|
|
787
|
+
/** What they wrote. */
|
|
788
|
+
message: string;
|
|
789
|
+
/** Lands on the customer record beside the email. */
|
|
790
|
+
name?: string;
|
|
791
|
+
/**
|
|
792
|
+
* Everything else the form asked, as label to answer. Each goes above the
|
|
793
|
+
* message as a line of its own, so the team reads the form the way it was
|
|
794
|
+
* filled in:
|
|
795
|
+
*
|
|
796
|
+
* { Topic: "Billing", "Order number": "4821" }
|
|
797
|
+
*
|
|
798
|
+
* Empty answers are left off.
|
|
799
|
+
*/
|
|
800
|
+
fields?: Record<string, string>;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Sending a contact message, for a form of the host's own.
|
|
805
|
+
*
|
|
806
|
+
* The same door the widget's composer uses, on the same session token, so
|
|
807
|
+
* what a host's form sends starts the thread the corner would have started:
|
|
808
|
+
* the team answers it from the inbox, the agent goes first, and with no
|
|
809
|
+
* panel open to watch, the reply goes to the email given. Somebody who
|
|
810
|
+
* writes here and later opens the widget finds their own conversation.
|
|
811
|
+
*
|
|
812
|
+
* A stateless HTTP client rather than the subscribing one, because nothing
|
|
813
|
+
* is read back: a form is a thing you hand over, not a thread you watch.
|
|
814
|
+
*/
|
|
815
|
+
/** What a send is told about who is sending and from where. */
|
|
816
|
+
type MedianContactOptions = {
|
|
817
|
+
/** The browser-safe median_pk_ id. */
|
|
818
|
+
apiKey: string;
|
|
819
|
+
/**
|
|
820
|
+
* Who is signed in on your site, if anyone. What the form says about the
|
|
821
|
+
* email and the name wins over this; the rest is shown to your team.
|
|
822
|
+
*/
|
|
823
|
+
user?: MedianUser;
|
|
824
|
+
/**
|
|
825
|
+
* Whether the message carries what the browser already knows: time zone,
|
|
826
|
+
* language, browser, OS, device, screen, and the page it was sent from.
|
|
827
|
+
* On by default. Pass `false` and none of it is read at all.
|
|
828
|
+
*/
|
|
829
|
+
telemetry?: boolean;
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* Send a contact message from a form of your own, on any page in the
|
|
833
|
+
* browser:
|
|
834
|
+
*
|
|
835
|
+
* await sendMedianContact(
|
|
836
|
+
* { apiKey: "median_pk_..." },
|
|
837
|
+
* { email, message, name, fields: { Topic: topic } },
|
|
838
|
+
* );
|
|
839
|
+
*
|
|
840
|
+
* It resolves once the message is in your inbox and rejects with an `Error`
|
|
841
|
+
* whose `message` is worth showing above your button. Nothing about the
|
|
842
|
+
* form is ours: any `<form>`, any framework, any styling.
|
|
843
|
+
*
|
|
844
|
+
* In React, `useMedianContact()` is the same call with `isSending` and
|
|
845
|
+
* `error` kept for you, and an identity route resolved.
|
|
846
|
+
*/
|
|
847
|
+
declare function sendMedianContact(options: MedianContactOptions, message: MedianContactMessage): Promise<void>;
|
|
848
|
+
|
|
849
|
+
type MedianContactHookOptions = {
|
|
850
|
+
/** Who is signed in on your site, if anyone. */
|
|
851
|
+
user?: MedianUser;
|
|
852
|
+
/** Browser context on the message. On by default. */
|
|
853
|
+
telemetry?: boolean;
|
|
854
|
+
/**
|
|
855
|
+
* Watch what goes wrong on the page, so a bug report filed from the
|
|
856
|
+
* message arrives with the error already attached. The same switch
|
|
857
|
+
* `<MedianSupport>` takes, and the same buffer behind it.
|
|
858
|
+
*/
|
|
859
|
+
diagnostics?: MedianDiagnostics;
|
|
860
|
+
/** Anything about your own app worth having next to a bug report. */
|
|
861
|
+
appState?: Record<string, string | number | boolean>;
|
|
862
|
+
};
|
|
863
|
+
/**
|
|
864
|
+
* Two ways in, the same two every component takes: the publishable key, or
|
|
865
|
+
* the path of a route built with `medianIdentity`, which answers with the
|
|
866
|
+
* key and whoever is signed in.
|
|
867
|
+
*/
|
|
868
|
+
type UseMedianContactOptions = MedianContactHookOptions & ({
|
|
869
|
+
apiKey: string;
|
|
870
|
+
identity?: string;
|
|
871
|
+
} | {
|
|
872
|
+
apiKey?: string;
|
|
873
|
+
identity: string;
|
|
874
|
+
});
|
|
875
|
+
/** What the hook hands back: one call, and the state around it. */
|
|
876
|
+
type MedianContactControl = {
|
|
877
|
+
/**
|
|
878
|
+
* Send what the form holds. Resolves once the message is in the inbox.
|
|
879
|
+
* Rejects with an `Error` whose `message` is worth showing, and `error`
|
|
880
|
+
* holds the same words until the next send.
|
|
881
|
+
*/
|
|
882
|
+
send: (message: MedianContactMessage) => Promise<void>;
|
|
883
|
+
/** A send is in flight. Hold the button. */
|
|
884
|
+
isSending: boolean;
|
|
885
|
+
/** Why the last send failed, or null. Cleared by the next send. */
|
|
886
|
+
error: string | null;
|
|
887
|
+
/**
|
|
888
|
+
* Whether a send has somewhere to go. False on the server, and for the
|
|
889
|
+
* beat before an identity route has answered with the key.
|
|
890
|
+
*/
|
|
891
|
+
isReady: boolean;
|
|
892
|
+
/**
|
|
893
|
+
* Who is typing, as far as the page knows: what you passed, what the
|
|
894
|
+
* identity route said, and the email the widget's own question was
|
|
895
|
+
* answered with on this browser. For filling the boxes in.
|
|
896
|
+
*/
|
|
897
|
+
user: MedianUser | undefined;
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* A contact form of your own, on Median's inbox:
|
|
901
|
+
*
|
|
902
|
+
* const contact = useMedianContact({ apiKey: "median_pk_..." });
|
|
903
|
+
*
|
|
904
|
+
* <form
|
|
905
|
+
* onSubmit={async (event) => {
|
|
906
|
+
* event.preventDefault();
|
|
907
|
+
* await contact.send({ email, message });
|
|
908
|
+
* }}
|
|
909
|
+
* >
|
|
910
|
+
* ...
|
|
911
|
+
* {contact.error && <p>{contact.error}</p>}
|
|
912
|
+
* <button disabled={contact.isSending}>Send</button>
|
|
913
|
+
* </form>
|
|
914
|
+
*
|
|
915
|
+
* Everything about the form is yours. What is sent starts a conversation in
|
|
916
|
+
* your inbox exactly as `<MedianContactForm>` and the widget do, and the
|
|
917
|
+
* reply goes to the email given. No provider, no connection to manage.
|
|
918
|
+
*/
|
|
919
|
+
declare function useMedianContact(options: UseMedianContactOptions): MedianContactControl;
|
|
920
|
+
|
|
921
|
+
export { type MedianContactControl, type MedianContactField, type MedianContactFieldKind, MedianContactForm, type MedianContactFormInstance, type MedianContactFormMountOptions, type MedianContactFormProps, type MedianContactMessage, type MedianContactOptions, type MedianDiagnostics, type MedianErrorReportOptions, type MedianErrorReportOutcome, MedianFeedback, type MedianFeedbackControl, type MedianFeedbackInstance, type MedianFeedbackProps, MedianSupport, type MedianSupportControl, type MedianSupportInstance, MedianSupportModal, type MedianSupportModalInstance, type MedianSupportModalProps, type MedianSupportProps, type MedianUser, type MedianDiagnostics as PortalDiagnostics, type MedianErrorReportOptions as PortalErrorReportOptions, type MedianErrorReportOutcome as PortalErrorReportOutcome, MedianFeedback as PortalFeedback, type MedianFeedbackControl as PortalFeedbackControl, type MedianFeedbackInstance as PortalFeedbackInstance, type MedianFeedbackProps as PortalFeedbackProps, MedianSupport as PortalSupport, type MedianSupportControl as PortalSupportControl, type MedianSupportInstance as PortalSupportInstance, type MedianSupportProps as PortalSupportProps, type MedianUser as PortalUser, type SupportLauncherKind, type UseMedianContactOptions, medianFeedback, medianSupport, mountMedianContactForm, mountMedianFeedback, mountMedianSupport, mountMedianSupportModal, mountMedianFeedback as mountPortalFeedback, mountMedianSupport as mountPortalSupport, medianFeedback as portalFeedback, medianSupport as portalSupport, reportError, sendMedianContact, useCanReportError, useMedianContact, useMedianFeedback, useMedianSupport, useMedianFeedback as usePortalFeedback, useMedianSupport as usePortalSupport };
|