@flung.dev/connect 0.1.1 → 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/README.md +222 -12
- package/dist/core/client.d.ts +9 -2
- package/dist/core/client.js +29 -10
- package/dist/core/controller.d.ts +214 -0
- package/dist/core/controller.js +407 -0
- package/dist/core/events.d.ts +25 -0
- package/dist/core/events.js +1 -0
- package/dist/core/index.d.ts +10 -4
- package/dist/core/index.js +6 -3
- package/dist/core/labels.d.ts +78 -0
- package/dist/core/labels.js +41 -0
- package/dist/core/platforms.d.ts +7 -1
- package/dist/core/platforms.js +11 -1
- package/dist/core/types.d.ts +20 -0
- package/dist/core/types.js +1 -0
- package/dist/element/copy.d.ts +3 -33
- package/dist/element/copy.js +2 -29
- package/dist/element/dom.d.ts +25 -5
- package/dist/element/dom.js +49 -5
- package/dist/element/events.d.ts +14 -18
- package/dist/element/flung-connect.d.ts +21 -1
- package/dist/element/flung-connect.js +221 -190
- package/dist/element/marks.d.ts +17 -0
- package/dist/element/marks.js +35 -0
- package/dist/element/styles.d.ts +6 -4
- package/dist/element/styles.js +195 -52
- package/dist/element/tooltip.d.ts +26 -0
- package/dist/element/tooltip.js +135 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +0 -1
- package/dist/react/index.d.ts +53 -3
- package/dist/react/index.js +40 -2
- package/dist/react/use-flung-connect.d.ts +13 -0
- package/dist/react/use-flung-connect.js +65 -0
- package/package.json +1 -1
package/dist/element/styles.js
CHANGED
|
@@ -1,75 +1,218 @@
|
|
|
1
|
+
import { PLATFORMS } from '../core/types.js';
|
|
2
|
+
import { PLATFORM_MARKS } from './marks.js';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
4
|
+
* How much of its tile each mark fills, so they read at one optical size. Solid discs and squares fill their whole grid
|
|
5
|
+
* and look heavier than open or wide marks, so they are drawn a little smaller.
|
|
6
|
+
*/
|
|
7
|
+
const OPTICAL_SCALE = {
|
|
8
|
+
x: 0.86,
|
|
9
|
+
instagram: 0.9,
|
|
10
|
+
facebook: 0.92,
|
|
11
|
+
threads: 0.9,
|
|
12
|
+
linkedin: 0.92,
|
|
13
|
+
tiktok: 0.9,
|
|
14
|
+
youtube: 1,
|
|
15
|
+
pinterest: 0.92,
|
|
16
|
+
bluesky: 1,
|
|
17
|
+
mastodon: 0.9,
|
|
18
|
+
discord: 1,
|
|
19
|
+
farcaster: 0.86,
|
|
20
|
+
};
|
|
21
|
+
/** Each platform's brand colour for its mark, and its optical scale. A black mark takes the text colour, so it shows on a dark page too. */
|
|
22
|
+
const MARKS = PLATFORMS.map((platform) => {
|
|
23
|
+
const hex = PLATFORM_MARKS[platform].hex;
|
|
24
|
+
return `.platform[data-platform="${platform}"] { --mark: ${hex === '000000' ? 'var(--flung-text)' : `#${hex}`}; --mark-scale: ${OPTICAL_SCALE[platform]}; }`;
|
|
25
|
+
}).join('\n');
|
|
26
|
+
const DARK = `
|
|
27
|
+
--flung-text: #f4f4f5;
|
|
28
|
+
--flung-muted: #a1a1aa;
|
|
29
|
+
--flung-surface: #18181b;
|
|
30
|
+
--flung-raised: #27272a;
|
|
31
|
+
--flung-border: #2e2e33;
|
|
32
|
+
--flung-accent: #f4f4f5;
|
|
33
|
+
--flung-accent-text: #18181b;
|
|
34
|
+
--flung-danger: #f87171;
|
|
35
|
+
--flung-warning: #fbbf24;
|
|
36
|
+
--flung-success: #4ade80;
|
|
37
|
+
--flung-mark-strength: 75%;
|
|
38
|
+
--flung-tint-strength: 14%;`;
|
|
39
|
+
/** What `unstyled` keeps: the element is a block, and `hidden` hides it. Everything else is the page's, through `::part()`. */
|
|
40
|
+
export const BASE_STYLES = `
|
|
41
|
+
:host { display: block; }
|
|
42
|
+
:host([hidden]) { display: none; }
|
|
43
|
+
[hidden] { display: none !important; }
|
|
44
|
+
`;
|
|
45
|
+
/**
|
|
46
|
+
* The panel's look, inside its shadow root. Colours, type, spacing, sizes, radii, borders, shadows and the focus ring
|
|
47
|
+
* are `--flung-*` properties a page can set on the element; each piece has a `part` name for `::part()`. Follows the
|
|
48
|
+
* page's light or dark scheme unless `theme` says which. `layout` picks `list`, `grid` or `compact`.
|
|
5
49
|
*/
|
|
6
50
|
export const STYLES = `
|
|
7
51
|
:host {
|
|
8
52
|
--flung-font: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
53
|
+
--flung-font-size: 15px;
|
|
54
|
+
--flung-font-size-small: 13px;
|
|
55
|
+
--flung-line-height: 1.45;
|
|
56
|
+
--flung-font-weight-medium: 500;
|
|
57
|
+
--flung-font-weight-strong: 600;
|
|
9
58
|
--flung-text: #18181b;
|
|
10
|
-
--flung-muted: #
|
|
59
|
+
--flung-muted: #71717a;
|
|
11
60
|
--flung-surface: #ffffff;
|
|
12
61
|
--flung-raised: #f4f4f5;
|
|
13
|
-
--flung-border: #
|
|
62
|
+
--flung-border: #e4e4e7;
|
|
14
63
|
--flung-accent: #18181b;
|
|
15
64
|
--flung-accent-text: #ffffff;
|
|
16
|
-
--flung-danger: #
|
|
17
|
-
--flung-warning: #
|
|
18
|
-
--flung-
|
|
65
|
+
--flung-danger: #dc2626;
|
|
66
|
+
--flung-warning: #b45309;
|
|
67
|
+
--flung-success: #15803d;
|
|
68
|
+
--flung-radius: 12px;
|
|
69
|
+
--flung-radius-inner: max(calc(var(--flung-radius) - 4px), 0px);
|
|
70
|
+
--flung-border-width: 1px;
|
|
71
|
+
--flung-shadow: none;
|
|
72
|
+
--flung-card-shadow: none;
|
|
73
|
+
--flung-padding: 16px;
|
|
74
|
+
--flung-gap: 12px;
|
|
75
|
+
--flung-badge-size: 40px;
|
|
76
|
+
--flung-mark-size: 20px;
|
|
77
|
+
--flung-mark-strength: 100%;
|
|
78
|
+
--flung-tint-strength: 8%;
|
|
79
|
+
--flung-avatar-size: 32px;
|
|
80
|
+
--flung-button-height: 36px;
|
|
81
|
+
--flung-button-padding: 0 14px;
|
|
82
|
+
--flung-button-radius: var(--flung-radius-inner);
|
|
83
|
+
--flung-focus-color: var(--flung-accent);
|
|
84
|
+
--flung-focus-width: 2px;
|
|
85
|
+
--flung-focus-offset: 2px;
|
|
86
|
+
--flung-grid-min: 16rem;
|
|
19
87
|
display: block;
|
|
20
|
-
font:
|
|
88
|
+
font: var(--flung-font-size)/var(--flung-line-height) var(--flung-font);
|
|
21
89
|
color: var(--flung-text);
|
|
90
|
+
-webkit-font-smoothing: antialiased;
|
|
22
91
|
}
|
|
23
92
|
:host([hidden]) { display: none; }
|
|
93
|
+
[hidden] { display: none !important; }
|
|
24
94
|
@media (prefers-color-scheme: dark) {
|
|
25
|
-
:host(:not([theme="light"])) {
|
|
26
|
-
--flung-text: #f4f4f5;
|
|
27
|
-
--flung-muted: #a1a1aa;
|
|
28
|
-
--flung-surface: #18181b;
|
|
29
|
-
--flung-raised: #27272a;
|
|
30
|
-
--flung-border: #3f3f46;
|
|
31
|
-
--flung-accent: #f4f4f5;
|
|
32
|
-
--flung-accent-text: #18181b;
|
|
33
|
-
--flung-danger: #f87171;
|
|
34
|
-
--flung-warning: #facc15;
|
|
95
|
+
:host(:not([theme="light"])) {${DARK}
|
|
35
96
|
}
|
|
36
97
|
}
|
|
37
|
-
:host([theme="dark"]) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
--flung-
|
|
41
|
-
--flung-
|
|
42
|
-
--flung-
|
|
43
|
-
--flung-
|
|
44
|
-
--flung-
|
|
45
|
-
--flung-
|
|
46
|
-
--flung-
|
|
98
|
+
:host([theme="dark"]) {${DARK}
|
|
99
|
+
}
|
|
100
|
+
:host([layout="compact"]) {
|
|
101
|
+
--flung-font-size: 14px;
|
|
102
|
+
--flung-font-size-small: 12px;
|
|
103
|
+
--flung-padding: 12px;
|
|
104
|
+
--flung-gap: 8px;
|
|
105
|
+
--flung-badge-size: 30px;
|
|
106
|
+
--flung-mark-size: 16px;
|
|
107
|
+
--flung-avatar-size: 24px;
|
|
108
|
+
--flung-button-height: 30px;
|
|
109
|
+
--flung-button-padding: 0 10px;
|
|
47
110
|
}
|
|
48
111
|
* { box-sizing: border-box; }
|
|
49
|
-
.panel { background: var(--flung-surface); border:
|
|
112
|
+
.panel { background: var(--flung-surface); border: var(--flung-border-width) solid var(--flung-border); border-radius: var(--flung-radius); box-shadow: var(--flung-shadow); overflow: hidden; }
|
|
50
113
|
ul { list-style: none; margin: 0; padding: 0; }
|
|
51
|
-
.platform { padding:
|
|
114
|
+
.platform { padding: var(--flung-gap) var(--flung-padding); border-top: var(--flung-border-width) solid var(--flung-border); }
|
|
52
115
|
.platform:first-child { border-top: 0; }
|
|
53
|
-
.row { display: flex; align-items: center; gap:
|
|
54
|
-
.
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
.
|
|
64
|
-
.
|
|
65
|
-
.
|
|
66
|
-
|
|
116
|
+
.row { display: flex; align-items: center; gap: var(--flung-gap); min-height: var(--flung-badge-size); }
|
|
117
|
+
.row ::slotted(*) { flex: 1; min-width: 0; }
|
|
118
|
+
.badge {
|
|
119
|
+
flex: none; width: var(--flung-badge-size); height: var(--flung-badge-size); border-radius: var(--flung-radius-inner);
|
|
120
|
+
display: grid; place-items: center;
|
|
121
|
+
color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
|
|
122
|
+
background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
|
|
123
|
+
border: var(--flung-border-width) solid color-mix(in srgb, var(--mark, var(--flung-text)) 14%, var(--flung-border));
|
|
124
|
+
}
|
|
125
|
+
.mark { width: var(--flung-mark-size); height: var(--flung-mark-size); fill: currentColor; display: block; transform: scale(var(--mark-scale, 1)); }
|
|
126
|
+
.title { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
127
|
+
.name { font-weight: var(--flung-font-weight-strong); line-height: 1.25; }
|
|
128
|
+
.count { font-size: var(--flung-font-size-small); color: var(--flung-muted); line-height: 1.3; }
|
|
129
|
+
.count.on::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--flung-success); margin-right: 6px; vertical-align: 2px; }
|
|
130
|
+
.accounts { margin: 10px 0 2px calc(var(--flung-badge-size) + var(--flung-gap)); border-left: var(--flung-border-width) solid var(--flung-border); }
|
|
131
|
+
.account { display: flex; align-items: center; gap: 10px; padding: 6px 0 6px 12px; min-height: calc(var(--flung-avatar-size) + 12px); }
|
|
132
|
+
.account ::slotted(*) { flex: 1; min-width: 0; }
|
|
133
|
+
.avatar {
|
|
134
|
+
flex: none; width: var(--flung-avatar-size); height: var(--flung-avatar-size); border-radius: 50%; object-fit: cover; display: block;
|
|
135
|
+
background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
|
|
136
|
+
border: var(--flung-border-width) solid color-mix(in srgb, var(--mark, var(--flung-text)) 14%, var(--flung-border));
|
|
137
|
+
}
|
|
138
|
+
.avatar.initial {
|
|
139
|
+
display: grid; place-items: center; font-size: calc(var(--flung-avatar-size) * .42); font-weight: var(--flung-font-weight-strong); line-height: 1;
|
|
140
|
+
color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
|
|
141
|
+
}
|
|
142
|
+
.who { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 2px; }
|
|
143
|
+
.names { min-width: 0; max-width: 100%; display: flex; flex-direction: column; }
|
|
144
|
+
.handle, .username { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
145
|
+
.handle { font-size: calc(var(--flung-font-size) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1.3; }
|
|
146
|
+
.username { font-size: var(--flung-font-size-small); color: var(--flung-muted); line-height: 1.3; }
|
|
147
|
+
.status { display: inline-flex; align-items: center; gap: 5px; font-size: calc(var(--flung-font-size-small) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1; padding: 4px 8px; border-radius: 999px; color: var(--flung-warning); background: color-mix(in srgb, var(--flung-warning) 12%, var(--flung-surface)); white-space: nowrap; }
|
|
148
|
+
.actions { flex: none; display: flex; align-items: center; gap: 4px; }
|
|
149
|
+
.tag { display: inline-flex; align-items: center; font-size: calc(var(--flung-font-size-small) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1; padding: 3px 7px; border-radius: 999px; color: var(--flung-muted); background: var(--flung-raised); border: var(--flung-border-width) solid var(--flung-border); white-space: nowrap; }
|
|
150
|
+
.note { margin: 10px 0 0 calc(var(--flung-badge-size) + var(--flung-gap)); color: var(--flung-muted); font-size: calc(var(--flung-font-size) - 1px); display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
151
|
+
.note-text { flex: 1 1 12rem; min-width: 0; }
|
|
152
|
+
.note a { color: var(--flung-text); font-weight: var(--flung-font-weight-medium); }
|
|
153
|
+
.spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--flung-border); border-top-color: var(--flung-text); flex: none; }
|
|
154
|
+
.confirm, .form { margin: 10px 0 0 calc(var(--flung-badge-size) + var(--flung-gap)); padding: var(--flung-gap); border-radius: var(--flung-radius-inner); background: var(--flung-raised); display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
155
|
+
.confirm p { flex-basis: 100%; margin: 0 0 2px; font-size: calc(var(--flung-font-size) - 1px); }
|
|
156
|
+
.choose { margin: 10px 0 0 calc(var(--flung-badge-size) + var(--flung-gap)); padding: var(--flung-gap); border-radius: var(--flung-radius-inner); background: var(--flung-raised); display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
|
|
157
|
+
.choose-title { margin: 0; font-size: calc(var(--flung-font-size) - 1px); }
|
|
158
|
+
.options { align-self: stretch; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 8px; }
|
|
159
|
+
button.option { height: auto; min-height: var(--flung-button-height); padding: 10px 12px; gap: 10px; justify-content: flex-start; text-align: left; white-space: normal; line-height: var(--flung-line-height); }
|
|
160
|
+
button.option:hover:not(:disabled) { background: var(--flung-surface); border-color: color-mix(in srgb, var(--mark, var(--flung-text)) 45%, var(--flung-border)); }
|
|
161
|
+
.option-icon {
|
|
162
|
+
flex: none; width: 32px; height: 32px; border-radius: var(--flung-radius-inner); display: grid; place-items: center;
|
|
163
|
+
color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
|
|
164
|
+
background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
|
|
165
|
+
}
|
|
166
|
+
.option-mark { width: 18px; height: 18px; fill: currentColor; display: block; }
|
|
167
|
+
.option-text { min-width: 0; display: flex; flex-direction: column; }
|
|
168
|
+
.option-name { font-weight: var(--flung-font-weight-strong); line-height: 1.3; }
|
|
169
|
+
.option-description { font-size: var(--flung-font-size-small); font-weight: normal; color: var(--flung-muted); line-height: 1.3; }
|
|
170
|
+
.form label { flex-basis: 100%; font-size: var(--flung-font-size-small); color: var(--flung-muted); }
|
|
171
|
+
.form input { flex: 1 1 12rem; min-width: 0; height: var(--flung-button-height); font: inherit; font-size: calc(var(--flung-font-size) - 1px); color: inherit; background: var(--flung-surface); border: var(--flung-border-width) solid var(--flung-border); border-radius: var(--flung-button-radius); padding: 0 10px; }
|
|
172
|
+
button {
|
|
173
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
174
|
+
height: var(--flung-button-height); padding: var(--flung-button-padding); border-radius: var(--flung-button-radius);
|
|
175
|
+
font: inherit; font-size: calc(var(--flung-font-size) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1; white-space: nowrap;
|
|
176
|
+
cursor: pointer; color: var(--flung-text); background: var(--flung-surface); border: var(--flung-border-width) solid var(--flung-border);
|
|
177
|
+
}
|
|
178
|
+
button:hover:not(:disabled) { background: var(--flung-raised); }
|
|
179
|
+
button:active:not(:disabled) { background: color-mix(in srgb, var(--flung-text) 8%, var(--flung-raised)); }
|
|
67
180
|
button.primary { background: var(--flung-accent); border-color: var(--flung-accent); color: var(--flung-accent-text); }
|
|
68
|
-
button.
|
|
69
|
-
button:disabled {
|
|
70
|
-
button
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
.
|
|
74
|
-
|
|
181
|
+
button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-accent) 88%, var(--flung-surface)); }
|
|
182
|
+
button.primary:active:not(:disabled) { background: color-mix(in srgb, var(--flung-accent) 78%, var(--flung-surface)); }
|
|
183
|
+
button.quiet { border-color: transparent; background: transparent; color: var(--flung-muted); padding: 0 10px; }
|
|
184
|
+
button.quiet:hover:not(:disabled) { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 8%, transparent); }
|
|
185
|
+
button.quiet:active:not(:disabled) { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 14%, transparent); }
|
|
186
|
+
button.danger { background: var(--flung-danger); border-color: var(--flung-danger); color: #ffffff; }
|
|
187
|
+
button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-danger) 88%, #000000); }
|
|
188
|
+
button.danger:active:not(:disabled) { background: color-mix(in srgb, var(--flung-danger) 78%, #000000); }
|
|
189
|
+
button:disabled { opacity: .5; cursor: default; }
|
|
190
|
+
button:focus-visible, input:focus-visible, a:focus-visible { outline: var(--flung-focus-width) solid var(--flung-focus-color); outline-offset: var(--flung-focus-offset); }
|
|
191
|
+
.message { margin: 0; padding: 10px var(--flung-padding); border-top: var(--flung-border-width) solid var(--flung-border); color: var(--flung-muted); font-size: calc(var(--flung-font-size) - 1px); background: var(--flung-raised); }
|
|
192
|
+
.message.error { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 8%, var(--flung-surface)); }
|
|
193
|
+
.footer { margin: 0; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px var(--flung-padding); border-top: var(--flung-border-width) solid var(--flung-border); font-size: calc(var(--flung-font-size-small) - 1px); color: var(--flung-muted); }
|
|
194
|
+
.footer a { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; font-weight: var(--flung-font-weight-strong); }
|
|
195
|
+
.footer a:hover { color: var(--flung-text); }
|
|
196
|
+
.footer .flung { width: 14px; height: 14px; fill: currentColor; display: block; }
|
|
197
|
+
.panel[data-layout="grid"] .list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--flung-grid-min)), 1fr)); gap: var(--flung-gap); padding: var(--flung-padding); }
|
|
198
|
+
.panel[data-layout="grid"] .platform, .panel[data-layout="grid"] .platform:first-child { border: var(--flung-border-width) solid var(--flung-border); border-radius: var(--flung-radius-inner); box-shadow: var(--flung-card-shadow); padding: var(--flung-gap); }
|
|
199
|
+
.panel[data-layout="grid"] .accounts, .panel[data-layout="grid"] .note, .panel[data-layout="grid"] .confirm, .panel[data-layout="grid"] .form, .panel[data-layout="grid"] .choose { margin-left: 0; }
|
|
200
|
+
.panel[data-layout="grid"] .accounts { border-left: 0; border-top: var(--flung-border-width) solid var(--flung-border); margin-top: var(--flung-gap); padding-top: 4px; }
|
|
201
|
+
.panel[data-layout="grid"] .account { padding-left: 0; }
|
|
202
|
+
.panel[data-layout="compact"] .count { display: none; }
|
|
203
|
+
.panel[data-layout="compact"] .accounts, .panel[data-layout="compact"] .note, .panel[data-layout="compact"] .confirm, .panel[data-layout="compact"] .form, .panel[data-layout="compact"] .choose { margin-top: 6px; }
|
|
204
|
+
.panel[data-layout="compact"] .account { padding-top: 3px; padding-bottom: 3px; }
|
|
205
|
+
@media (max-width: 420px) {
|
|
206
|
+
.platform { padding: var(--flung-gap); }
|
|
207
|
+
.accounts, .note, .confirm, .form, .choose { margin-left: 0; }
|
|
208
|
+
.accounts { border-left: 0; border-top: var(--flung-border-width) solid var(--flung-border); margin-top: 10px; }
|
|
209
|
+
.account { padding-left: 0; }
|
|
210
|
+
.row button { padding: 0 12px; }
|
|
211
|
+
}
|
|
212
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
213
|
+
button { transition: background-color .15s ease, color .15s ease, border-color .15s ease, opacity .15s ease; }
|
|
214
|
+
.spinner { animation: flung-spin .8s linear infinite; }
|
|
215
|
+
}
|
|
216
|
+
@keyframes flung-spin { to { transform: rotate(360deg); } }
|
|
217
|
+
${MARKS}
|
|
75
218
|
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel's own tooltip, never the browser's `title`: that one cannot be styled or themed, opens with no keyboard or
|
|
3
|
+
* finger, and looks different in every browser. One tooltip serves the whole panel. An element asks for one with
|
|
4
|
+
* `data-tooltip` (the words); with `data-tooltip-truncated` as well it opens only while the element's text is cut short,
|
|
5
|
+
* as an account's name is in a narrow panel.
|
|
6
|
+
*
|
|
7
|
+
* It opens after a short rest of the pointer, on keyboard focus and on a held finger, and closes on Escape, a press, a
|
|
8
|
+
* scroll, or when the pointer and focus leave. It goes in the top layer where the browser has popovers, so no page's
|
|
9
|
+
* `overflow` or `transform` cuts it off, above the element (below when there is no room), and inside the screen's edges.
|
|
10
|
+
*/
|
|
11
|
+
/** How long the pointer rests before the tooltip opens. */
|
|
12
|
+
export declare const TOOLTIP_DELAY_MS = 400;
|
|
13
|
+
/** How long a finger holds an element before its tooltip opens. */
|
|
14
|
+
export declare const TOOLTIP_HOLD_MS = 500;
|
|
15
|
+
/** How long a tooltip opened by a finger stays once it lifts. */
|
|
16
|
+
export declare const TOOLTIP_TOUCH_MS = 2500;
|
|
17
|
+
/** The panel's tooltip, attached to its shadow root once. Put `element` back in the root each time the panel is drawn. */
|
|
18
|
+
export interface PanelTooltip {
|
|
19
|
+
readonly element: HTMLElement;
|
|
20
|
+
hide(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function panelTooltip(root: ShadowRoot): PanelTooltip;
|
|
23
|
+
/** What the tooltip needs to work at all, kept with `unstyled` too: fixed in place, out of the way of the pointer. */
|
|
24
|
+
export declare const TOOLTIP_BASE_STYLES = "\n.tooltip { position: fixed; inset: auto; margin: 0; pointer-events: none; }\n";
|
|
25
|
+
/** Its look: a small pill in the panel's own colours turned around, so it follows the theme and the page's overrides. */
|
|
26
|
+
export declare const TOOLTIP_STYLES = "\n:host {\n --flung-tooltip-background: var(--flung-text);\n --flung-tooltip-text: var(--flung-surface);\n --flung-tooltip-radius: var(--flung-radius-inner);\n --flung-tooltip-shadow: 0 4px 14px rgb(0 0 0 / .16);\n}\n.tooltip {\n z-index: 2147483647; max-width: min(20rem, calc(100vw - 16px)); padding: 4px 8px; border: 0; border-radius: var(--flung-tooltip-radius);\n background: var(--flung-tooltip-background); color: var(--flung-tooltip-text); box-shadow: var(--flung-tooltip-shadow);\n font: var(--flung-font-weight-medium) var(--flung-font-size-small)/1.35 var(--flung-font); overflow-wrap: anywhere;\n}\n@media (prefers-reduced-motion: no-preference) {\n .tooltip:not([hidden]) { animation: flung-tooltip-in .12s ease-out; }\n}\n@keyframes flung-tooltip-in { from { opacity: 0; } }\n";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel's own tooltip, never the browser's `title`: that one cannot be styled or themed, opens with no keyboard or
|
|
3
|
+
* finger, and looks different in every browser. One tooltip serves the whole panel. An element asks for one with
|
|
4
|
+
* `data-tooltip` (the words); with `data-tooltip-truncated` as well it opens only while the element's text is cut short,
|
|
5
|
+
* as an account's name is in a narrow panel.
|
|
6
|
+
*
|
|
7
|
+
* It opens after a short rest of the pointer, on keyboard focus and on a held finger, and closes on Escape, a press, a
|
|
8
|
+
* scroll, or when the pointer and focus leave. It goes in the top layer where the browser has popovers, so no page's
|
|
9
|
+
* `overflow` or `transform` cuts it off, above the element (below when there is no room), and inside the screen's edges.
|
|
10
|
+
*/
|
|
11
|
+
import { html } from './dom.js';
|
|
12
|
+
/** How long the pointer rests before the tooltip opens. */
|
|
13
|
+
export const TOOLTIP_DELAY_MS = 400;
|
|
14
|
+
/** How long a finger holds an element before its tooltip opens. */
|
|
15
|
+
export const TOOLTIP_HOLD_MS = 500;
|
|
16
|
+
/** How long a tooltip opened by a finger stays once it lifts. */
|
|
17
|
+
export const TOOLTIP_TOUCH_MS = 2500;
|
|
18
|
+
const GAP = 6;
|
|
19
|
+
const EDGE = 8;
|
|
20
|
+
/** Whether an element's text overflows its box. */
|
|
21
|
+
const isCut = (element) => element.scrollWidth > element.clientWidth + 1;
|
|
22
|
+
/** A pointer event's kind (`mouse`, `pen`, `touch`). A shadow root's event map names no pointer events, so it is read off the event. */
|
|
23
|
+
const pointerType = (event) => ('pointerType' in event && typeof event.pointerType === 'string' ? event.pointerType : 'mouse');
|
|
24
|
+
export function panelTooltip(root) {
|
|
25
|
+
const element = html('span', { class: 'tooltip', part: 'tooltip', 'aria-hidden': 'true', hidden: true });
|
|
26
|
+
const popover = typeof element.showPopover === 'function';
|
|
27
|
+
if (popover)
|
|
28
|
+
element.setAttribute('popover', 'manual');
|
|
29
|
+
let timer;
|
|
30
|
+
let anchor = null;
|
|
31
|
+
const asking = (target) => (target instanceof Element ? target.closest('[data-tooltip]') : null);
|
|
32
|
+
const place = (target) => {
|
|
33
|
+
const box = target.getBoundingClientRect();
|
|
34
|
+
const width = element.offsetWidth;
|
|
35
|
+
const height = element.offsetHeight;
|
|
36
|
+
const above = box.top - GAP - height >= EDGE;
|
|
37
|
+
const top = above ? box.top - GAP - height : box.bottom + GAP;
|
|
38
|
+
const left = Math.min(Math.max(box.left + box.width / 2 - width / 2, EDGE), Math.max(EDGE, window.innerWidth - EDGE - width));
|
|
39
|
+
element.style.left = `${left}px`;
|
|
40
|
+
element.style.top = `${top}px`;
|
|
41
|
+
element.dataset['side'] = above ? 'top' : 'bottom';
|
|
42
|
+
};
|
|
43
|
+
const show = (target) => {
|
|
44
|
+
const words = target.dataset['tooltip'] ?? '';
|
|
45
|
+
if (words === '' || !target.isConnected || (target.hasAttribute('data-tooltip-truncated') && !isCut(target)))
|
|
46
|
+
return;
|
|
47
|
+
anchor = target;
|
|
48
|
+
element.textContent = words;
|
|
49
|
+
element.hidden = false;
|
|
50
|
+
if (popover && !element.matches(':popover-open'))
|
|
51
|
+
element.showPopover();
|
|
52
|
+
place(target);
|
|
53
|
+
document.addEventListener('keydown', onKey);
|
|
54
|
+
window.addEventListener('scroll', hide, true);
|
|
55
|
+
window.addEventListener('resize', hide);
|
|
56
|
+
};
|
|
57
|
+
function hide() {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
timer = undefined;
|
|
60
|
+
if (anchor === null)
|
|
61
|
+
return;
|
|
62
|
+
anchor = null;
|
|
63
|
+
if (popover && element.matches(':popover-open'))
|
|
64
|
+
element.hidePopover();
|
|
65
|
+
element.hidden = true;
|
|
66
|
+
document.removeEventListener('keydown', onKey);
|
|
67
|
+
window.removeEventListener('scroll', hide, true);
|
|
68
|
+
window.removeEventListener('resize', hide);
|
|
69
|
+
}
|
|
70
|
+
function onKey(event) {
|
|
71
|
+
if (event.key === 'Escape')
|
|
72
|
+
hide();
|
|
73
|
+
}
|
|
74
|
+
const later = (target, delay) => {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
timer = setTimeout(() => show(target), delay);
|
|
77
|
+
};
|
|
78
|
+
root.addEventListener('pointerover', (event) => {
|
|
79
|
+
const target = asking(event.target);
|
|
80
|
+
if (target === null || target === anchor || pointerType(event) === 'touch')
|
|
81
|
+
return;
|
|
82
|
+
hide();
|
|
83
|
+
later(target, TOOLTIP_DELAY_MS);
|
|
84
|
+
});
|
|
85
|
+
root.addEventListener('pointerout', (event) => {
|
|
86
|
+
const target = asking(event.target);
|
|
87
|
+
if (target === null || ('relatedTarget' in event && event.relatedTarget instanceof Node && target.contains(event.relatedTarget)))
|
|
88
|
+
return;
|
|
89
|
+
hide();
|
|
90
|
+
});
|
|
91
|
+
root.addEventListener('pointerdown', (event) => {
|
|
92
|
+
hide();
|
|
93
|
+
const target = asking(event.target);
|
|
94
|
+
if (target !== null && pointerType(event) === 'touch')
|
|
95
|
+
later(target, TOOLTIP_HOLD_MS);
|
|
96
|
+
});
|
|
97
|
+
root.addEventListener('pointerup', (event) => {
|
|
98
|
+
if (pointerType(event) !== 'touch')
|
|
99
|
+
return;
|
|
100
|
+
if (anchor === null) {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
timer = setTimeout(hide, TOOLTIP_TOUCH_MS);
|
|
105
|
+
});
|
|
106
|
+
root.addEventListener('focusin', (event) => {
|
|
107
|
+
const target = asking(event.target);
|
|
108
|
+
if (target !== null)
|
|
109
|
+
show(target);
|
|
110
|
+
});
|
|
111
|
+
root.addEventListener('focusout', hide);
|
|
112
|
+
return { element, hide };
|
|
113
|
+
}
|
|
114
|
+
/** What the tooltip needs to work at all, kept with `unstyled` too: fixed in place, out of the way of the pointer. */
|
|
115
|
+
export const TOOLTIP_BASE_STYLES = `
|
|
116
|
+
.tooltip { position: fixed; inset: auto; margin: 0; pointer-events: none; }
|
|
117
|
+
`;
|
|
118
|
+
/** Its look: a small pill in the panel's own colours turned around, so it follows the theme and the page's overrides. */
|
|
119
|
+
export const TOOLTIP_STYLES = `
|
|
120
|
+
:host {
|
|
121
|
+
--flung-tooltip-background: var(--flung-text);
|
|
122
|
+
--flung-tooltip-text: var(--flung-surface);
|
|
123
|
+
--flung-tooltip-radius: var(--flung-radius-inner);
|
|
124
|
+
--flung-tooltip-shadow: 0 4px 14px rgb(0 0 0 / .16);
|
|
125
|
+
}
|
|
126
|
+
.tooltip {
|
|
127
|
+
z-index: 2147483647; max-width: min(20rem, calc(100vw - 16px)); padding: 4px 8px; border: 0; border-radius: var(--flung-tooltip-radius);
|
|
128
|
+
background: var(--flung-tooltip-background); color: var(--flung-tooltip-text); box-shadow: var(--flung-tooltip-shadow);
|
|
129
|
+
font: var(--flung-font-weight-medium) var(--flung-font-size-small)/1.35 var(--flung-font); overflow-wrap: anywhere;
|
|
130
|
+
}
|
|
131
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
132
|
+
.tooltip:not([hidden]) { animation: flung-tooltip-in .12s ease-out; }
|
|
133
|
+
}
|
|
134
|
+
@keyframes flung-tooltip-in { from { opacity: 0; } }
|
|
135
|
+
`;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
* or call `defineFlungConnect()`.
|
|
5
5
|
*/
|
|
6
6
|
export { defineFlungConnect, TAG_NAME } from './element/define.js';
|
|
7
|
-
export type { FlungConnectElement } from './element/flung-connect.js';
|
|
8
|
-
export {
|
|
9
|
-
export type { ConnectLabels } from './element/copy.js';
|
|
10
|
-
export type { ConnectDetail, DisconnectDetail, ErrorDetail, FlungConnectEventMap, LoadDetail } from './element/events.js';
|
|
7
|
+
export type { FlungConnectElement, FlungConnectLayout, FlungConnectVariants } from './element/flung-connect.js';
|
|
8
|
+
export type { ChangeDetail, FlungConnectEventMap } from './element/events.js';
|
|
11
9
|
export * from './core/index.js';
|
package/dist/index.js
CHANGED
package/dist/react/index.d.ts
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
|
-
import type { CSSProperties } from 'react';
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import type { TokenSource } from '../core/client.js';
|
|
3
|
+
import type { AccountItem, ConnectActions, PlatformItem } from '../core/controller.js';
|
|
4
|
+
import type { ConnectLabels } from '../core/labels.js';
|
|
3
5
|
import type { Platform } from '../core/types.js';
|
|
4
|
-
import type { ConnectLabels } from '../element/copy.js';
|
|
5
6
|
import type { ConnectDetail, DisconnectDetail, ErrorDetail, LoadDetail } from '../element/events.js';
|
|
6
|
-
import type { FlungConnectElement } from '../element/flung-connect.js';
|
|
7
|
+
import type { FlungConnectElement, FlungConnectLayout, FlungConnectVariants } from '../element/flung-connect.js';
|
|
8
|
+
export { useFlungConnect } from './use-flung-connect.js';
|
|
9
|
+
export type { UseFlungConnectOptions, UseFlungConnectResult } from './use-flung-connect.js';
|
|
10
|
+
/** What `renderPlatform` is given: the platform, the actions, and the words in use. */
|
|
11
|
+
export interface PlatformRowProps {
|
|
12
|
+
/** The platform, its accounts and what it is doing. */
|
|
13
|
+
platform: PlatformItem;
|
|
14
|
+
/** The panel's actions. Call `connect` straight from a click: a browser opens a window only during one. */
|
|
15
|
+
actions: ConnectActions;
|
|
16
|
+
/** The panel's words, defaults filled in. */
|
|
17
|
+
labels: ConnectLabels;
|
|
18
|
+
}
|
|
19
|
+
/** What `renderAccount` is given: the account, its platform, the actions, and the words in use. */
|
|
20
|
+
export interface AccountRowProps {
|
|
21
|
+
/** The account and what to show for it. */
|
|
22
|
+
account: AccountItem;
|
|
23
|
+
/** The platform it is on. */
|
|
24
|
+
platform: PlatformItem;
|
|
25
|
+
/** The panel's actions, such as `reconnect(account.account.id)` and `requestDisconnect(account.account.id)`. */
|
|
26
|
+
actions: ConnectActions;
|
|
27
|
+
/** The panel's words, defaults filled in. */
|
|
28
|
+
labels: ConnectLabels;
|
|
29
|
+
}
|
|
7
30
|
/**
|
|
8
31
|
* `<FlungConnect>`: the `<flung-connect>` element for React. It registers the element on first render, passes plain
|
|
9
32
|
* values as attributes, sets `token` and `labels` as properties, and turns the element's events into callbacks. React
|
|
@@ -18,13 +41,40 @@ export interface FlungConnectProps {
|
|
|
18
41
|
platforms?: readonly Platform[] | undefined;
|
|
19
42
|
/** `redirect` sends the whole page to the platform and back, for pages that cannot open windows. */
|
|
20
43
|
mode?: 'popup' | 'redirect' | undefined;
|
|
44
|
+
/** `light` or `dark`. Follows the page's colour scheme when left out. */
|
|
21
45
|
theme?: 'light' | 'dark' | undefined;
|
|
46
|
+
/** `list` (the default), `grid` or `compact`. */
|
|
47
|
+
layout?: FlungConnectLayout | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* How a platform with a connect variant is offered. `choose` (the default) asks after Connect whether to connect a
|
|
50
|
+
* LinkedIn personal profile or a company page; `tiles` shows them as two platforms, LinkedIn and LinkedIn Page.
|
|
51
|
+
*/
|
|
52
|
+
variants?: FlungConnectVariants | undefined;
|
|
53
|
+
/** Drops Flung's stylesheet, so every `::part()` is yours to style. */
|
|
54
|
+
unstyled?: boolean | undefined;
|
|
55
|
+
/** `none` hides the small "Secured by Flung" line under the list. */
|
|
56
|
+
branding?: 'none' | undefined;
|
|
57
|
+
/** Any of the panel's words, to put it in another language. */
|
|
22
58
|
labels?: Partial<ConnectLabels> | undefined;
|
|
59
|
+
/** A class for the element itself. */
|
|
23
60
|
className?: string | undefined;
|
|
61
|
+
/** Styles for the element, such as its `--flung-*` custom properties. */
|
|
24
62
|
style?: CSSProperties | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Draws a platform's row (its mark, name and Connect button) yourself, in your page's own DOM and CSS. The panel keeps
|
|
65
|
+
* the accounts, the handle question, the wait and the confirm below it.
|
|
66
|
+
*/
|
|
67
|
+
renderPlatform?: ((props: PlatformRowProps) => ReactNode) | undefined;
|
|
68
|
+
/** Draws one account's row yourself, in your page's own DOM and CSS. The panel keeps the confirm and the messages. */
|
|
69
|
+
renderAccount?: ((props: AccountRowProps) => ReactNode) | undefined;
|
|
70
|
+
/** The accounts were read: on first show and after every change. */
|
|
25
71
|
onLoad?: ((detail: LoadDetail) => void) | undefined;
|
|
72
|
+
/** A connect or reconnect finished. */
|
|
26
73
|
onConnect?: ((detail: ConnectDetail) => void) | undefined;
|
|
74
|
+
/** An account was disconnected. */
|
|
27
75
|
onDisconnect?: ((detail: DisconnectDetail) => void) | undefined;
|
|
76
|
+
/** A call or a connect failed. */
|
|
28
77
|
onError?: ((detail: ErrorDetail) => void) | undefined;
|
|
29
78
|
}
|
|
79
|
+
/** The `<flung-connect>` element as a React component. */
|
|
30
80
|
export declare const FlungConnect: import("react").ForwardRefExoticComponent<FlungConnectProps & import("react").RefAttributes<FlungConnectElement>>;
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { createElement, forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef } from 'react';
|
|
1
|
+
import { createElement, forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
import { initialConnectState } from '../core/controller.js';
|
|
2
3
|
import { defineFlungConnect, TAG_NAME } from '../element/define.js';
|
|
4
|
+
export { useFlungConnect } from './use-flung-connect.js';
|
|
3
5
|
const EVENTS = { 'flung-load': 'onLoad', 'flung-connect': 'onConnect', 'flung-disconnect': 'onDisconnect', 'flung-error': 'onError' };
|
|
4
6
|
const useClientLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
5
7
|
function FlungConnectInner(props, ref) {
|
|
@@ -36,14 +38,50 @@ function FlungConnectInner(props, ref) {
|
|
|
36
38
|
if (node.token !== props.token)
|
|
37
39
|
node.token = props.token;
|
|
38
40
|
}, [props.token, props.labels]);
|
|
41
|
+
// The element's state, only when a render prop needs it, so a panel without them renders no more often than before.
|
|
42
|
+
const custom = props.renderPlatform !== undefined || props.renderAccount !== undefined;
|
|
43
|
+
const subscribe = useCallback((notify) => {
|
|
44
|
+
const node = element.current;
|
|
45
|
+
if (!custom || node === null)
|
|
46
|
+
return () => undefined;
|
|
47
|
+
node.addEventListener('flung-change', notify);
|
|
48
|
+
return () => node.removeEventListener('flung-change', notify);
|
|
49
|
+
}, [custom]);
|
|
50
|
+
const initial = initialConnectState(props.token !== null);
|
|
51
|
+
const state = useSyncExternalStore(subscribe, () => (custom ? (element.current?.state ?? initial) : initial), () => initial);
|
|
52
|
+
const actions = useMemo(() => {
|
|
53
|
+
const controller = () => element.current?.controller ?? null;
|
|
54
|
+
return {
|
|
55
|
+
connect: (platform, options) => controller()?.connect(platform, options),
|
|
56
|
+
submitHandle: (platform, handle) => controller()?.submitHandle(platform, handle),
|
|
57
|
+
reconnect: (accountId) => controller()?.reconnect(accountId),
|
|
58
|
+
requestDisconnect: (accountId) => controller()?.requestDisconnect(accountId),
|
|
59
|
+
confirmDisconnect: (platform) => controller()?.confirmDisconnect(platform) ?? Promise.resolve(),
|
|
60
|
+
cancel: (platform) => controller()?.cancel(platform),
|
|
61
|
+
dismissNotice: () => controller()?.dismissNotice(),
|
|
62
|
+
refresh: () => controller()?.refresh() ?? Promise.resolve(),
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
// Each custom row goes into the light DOM with the element's slot name for it, so your CSS reaches it directly.
|
|
66
|
+
const children = custom
|
|
67
|
+
? state.platforms.flatMap((platform) => [
|
|
68
|
+
props.renderPlatform === undefined ? null : createElement('div', { key: `platform-${platform.platform}`, slot: `platform-${platform.platform}` }, props.renderPlatform({ platform, actions, labels: state.labels })),
|
|
69
|
+
...(props.renderAccount === undefined ? [] : platform.accounts.map((account) => createElement('div', { key: `account-${account.account.id}`, slot: `account-${account.account.id}` }, props.renderAccount?.({ account, platform, actions, labels: state.labels })))),
|
|
70
|
+
])
|
|
71
|
+
: undefined;
|
|
39
72
|
return createElement(TAG_NAME, {
|
|
40
73
|
ref: element,
|
|
41
74
|
'base-url': props.baseUrl,
|
|
42
75
|
platforms: props.platforms?.join(','),
|
|
43
76
|
mode: props.mode,
|
|
44
77
|
theme: props.theme,
|
|
78
|
+
layout: props.layout,
|
|
79
|
+
variants: props.variants,
|
|
80
|
+
unstyled: props.unstyled === true ? '' : undefined,
|
|
81
|
+
branding: props.branding,
|
|
45
82
|
class: props.className,
|
|
46
83
|
style: props.style,
|
|
47
|
-
});
|
|
84
|
+
}, children);
|
|
48
85
|
}
|
|
86
|
+
/** The `<flung-connect>` element as a React component. */
|
|
49
87
|
export const FlungConnect = forwardRef(FlungConnectInner);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConnectActions, ConnectControllerOptions, ConnectState } from '../core/controller.js';
|
|
2
|
+
/** What `useFlungConnect` takes: the controller's options. Callbacks and a token function may be new on every render. */
|
|
3
|
+
export type UseFlungConnectOptions = ConnectControllerOptions;
|
|
4
|
+
/** What `useFlungConnect` returns: the state to draw, and the actions to call from your own buttons and forms. */
|
|
5
|
+
export type UseFlungConnectResult = ConnectState & ConnectActions;
|
|
6
|
+
/**
|
|
7
|
+
* The connect panel with none of Flung's markup, for drawing every piece with your own components and CSS. It runs a
|
|
8
|
+
* `createConnectController` for as long as the component is mounted, and a new one when the token string, `baseUrl`
|
|
9
|
+
* or whether there is a `fetch` changes. A token function, `fetch` and the callbacks are read fresh on every call, so
|
|
10
|
+
* they need no `useCallback`. Call `connect`, `submitHandle` and `reconnect` straight from a click or submit handler: a
|
|
11
|
+
* browser opens a window only during one.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useFlungConnect(options: UseFlungConnectOptions): UseFlungConnectResult;
|