@flung.dev/connect 0.1.2 → 0.4.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.
@@ -5,8 +5,13 @@ export interface PlatformMark {
5
5
  readonly hex: string;
6
6
  }
7
7
  export declare const PLATFORM_MARKS: Readonly<Record<Platform, PlatformMark>>;
8
+ /** The icons of the choice between a personal profile and a company page, on a 24 by 24 grid (Boxicons, MIT). */
9
+ export declare const CHOICE_MARKS: {
10
+ readonly personal: 'M12 2a5 5 0 1 0 5 5 5 5 0 0 0-5-5zm0 8a3 3 0 1 1 3-3 3 3 0 0 1-3 3zm9 11v-1a7 7 0 0 0-7-7h-4a7 7 0 0 0-7 7v1h2v-1a5 5 0 0 1 5-5h4a5 5 0 0 1 5 5v1z';
11
+ readonly pages: 'M19 2H9c-1.103 0-2 .897-2 2v6H5c-1.103 0-2 .897-2 2v9a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V4c0-1.103-.897-2-2-2zM5 12h6v8H5v-8zm14 8h-6v-8c0-1.103-.897-2-2-2H9V4h10v16zM11 6h2v2h-2zm4 0h2v2h-2zm0 4.031h2V12h-2zM15 14h2v2h-2zm-8 .001h2v2H7z';
12
+ };
8
13
  /** Flung's mark, from https://assets.flung.dev/brand/logo/flung-mark-on-light.svg. */
9
14
  export declare const FLUNG_MARK: {
10
- readonly viewBox: '0 0 500 500';
11
- readonly path: 'M 189.525 16.874 C 190.808 16.873 192.091 16.873 193.413 16.872 C 196.941 16.869 200.467 16.873 203.992 16.878 C 207.81 16.884 211.625 16.881 215.442 16.881 C 222.042 16.88 228.642 16.884 235.242 16.891 C 244.784 16.899 254.326 16.902 263.868 16.904 C 279.352 16.906 294.837 16.913 310.322 16.924 C 325.357 16.933 340.393 16.941 355.428 16.947 C 356.82 16.947 356.82 16.947 358.24 16.947 C 362.896 16.949 367.55 16.95 372.206 16.951 C 410.783 16.963 449.359 16.982 487.935 17.007 C 487.954 19.653 487.967 22.299 487.978 24.945 C 487.986 26.06 487.986 26.06 487.994 27.198 C 488.011 33.049 487.504 38.516 486.278 44.226 C 485.989 45.603 485.989 45.603 485.694 47.009 C 479.753 73.338 467.128 96.459 449.249 116.488 C 448.329 117.521 447.408 118.554 446.46 119.619 C 434.313 132.43 419.889 141.304 403.93 148.543 C 402.995 148.995 402.061 149.447 401.097 149.912 C 387.969 155.816 373.086 157.455 358.886 158.492 C 337.903 160.08 318.12 166.235 302.238 180.598 C 301.228 181.476 301.228 181.476 300.196 182.373 C 290.186 191.634 284.215 203.986 280.409 216.88 C 276.039 231.615 269.265 243.914 255.779 252.324 C 245.106 257.877 235.96 259.309 224.105 259.493 C 207.259 259.997 192.415 263.213 180.305 275.865 C 171.548 286.39 167.399 299.632 168.491 313.239 C 169.76 324.473 175.361 336.705 183.966 344.189 C 197.7 354.156 210.093 357.822 227.074 356.348 C 240.814 353.699 252.153 345.546 260.235 334.242 C 264.938 325.96 267.358 317.705 268.395 308.295 C 270.357 290.773 276.223 278.048 289.803 266.539 C 299.876 258.871 311.257 253.264 324.085 253.285 C 324.795 253.283 325.507 253.282 326.238 253.28 C 328.597 253.276 330.956 253.287 333.315 253.3 C 335.015 253.301 336.714 253.301 338.415 253.3 C 343.014 253.298 347.612 253.311 352.211 253.327 C 357.024 253.34 361.836 253.341 366.649 253.344 C 375.754 253.351 384.859 253.369 393.963 253.391 C 404.333 253.416 414.703 253.429 425.071 253.439 C 446.395 253.463 467.718 253.502 489.041 253.551 C 489.12 263.833 489.178 274.116 489.213 284.399 C 489.231 289.177 489.253 293.955 489.292 298.732 C 489.713 353.119 479.51 398.095 440.406 438.144 C 402.556 475.574 354.507 484.025 303.516 483.908 C 298.647 483.9 293.777 483.908 288.908 483.913 C 280.739 483.918 272.57 483.91 264.401 483.895 C 255.01 483.876 245.619 483.883 236.228 483.901 C 228.105 483.916 219.983 483.918 211.86 483.909 C 207.034 483.904 202.21 483.903 197.384 483.914 C 159.131 483.99 159.131 483.99 143.068 480.146 C 141.987 479.903 140.905 479.66 139.79 479.408 C 137.277 478.822 134.828 478.14 132.36 477.383 C 131.465 477.109 130.569 476.835 129.646 476.552 C 105.884 468.875 84.897 456.337 66.8 439.249 C 65.448 438.112 64.09 436.983 62.724 435.863 C 32.664 410.058 16.633 368.22 12.873 329.721 C 12.432 323.684 12.465 317.647 12.46 311.597 C 12.454 310.143 12.448 308.689 12.443 307.192 C 12.428 303.231 12.422 299.269 12.416 295.309 C 12.413 292.822 12.409 290.335 12.405 287.848 C 12.389 280.032 12.378 272.215 12.374 264.399 C 12.369 255.444 12.35 246.487 12.318 237.531 C 12.294 230.566 12.282 223.6 12.28 216.633 C 12.28 212.495 12.274 208.355 12.254 204.215 C 12.154 182.613 12.574 161.364 18.717 140.46 C 18.991 139.523 19.265 138.584 19.548 137.619 C 27.777 111.087 42.745 89.246 61.273 68.959 C 62.004 68.134 62.735 67.311 63.487 66.463 C 95.594 32.13 143.615 16.748 189.525 16.874 Z';
15
+ readonly viewBox: '-2 -2 36 36';
16
+ readonly path: 'M12.942 6.235L21.674 4.058A6.0 6.0 0 0 1 28.947 8.428L29.915 12.309A6.0 6.0 0 0 1 25.545 19.582L16.812 21.76A6.0 6.0 0 0 1 9.539 17.39L8.571 13.508A6.0 6.0 0 0 1 12.942 6.235ZM13.758 19.429L20.55 17.736L14.528 29.131Z M3 12.2H7.4 M2.2 18H5.4';
12
17
  };
@@ -24,5 +24,12 @@ export const PLATFORM_MARKS = {
24
24
  // Simple Icons farcaster (CC0 1.0)
25
25
  farcaster: { hex: '855DCD', path: 'M18.24.24H5.76C2.5789.24 0 2.8188 0 6v12c0 3.1811 2.5789 5.76 5.76 5.76h12.48c3.1812 0 5.76-2.5789 5.76-5.76V6C24 2.8188 21.4212.24 18.24.24m.8155 17.1662v.504c.2868-.0256.5458.1905.5439.479v.5688h-5.1437v-.5688c-.0019-.2885.2576-.5047.5443-.479v-.504c0-.22.1525-.402.358-.458l-.0095-4.3645c-.1589-1.7366-1.6402-3.0979-3.4435-3.0979-1.8038 0-3.2846 1.3613-3.4435 3.0979l-.0096 4.3578c.2276.0424.5318.2083.5395.4648v.504c.2863-.0256.5457.1905.5438.479v.5688H4.3915v-.5688c-.0019-.2885.2575-.5047.5438-.479v-.504c0-.2529.2011-.4548.4536-.4724v-7.895h-.4905L4.2898 7.008l2.6405-.0005V5.0419h9.9495v1.9656h2.8219l-.6091 2.0314h-.4901v7.8949c.2519.0177.453.2195.453.4724' },
26
26
  };
27
+ /** The icons of the choice between a personal profile and a company page, on a 24 by 24 grid (Boxicons, MIT). */
28
+ export const CHOICE_MARKS = {
29
+ // Boxicons bx-user
30
+ personal: 'M12 2a5 5 0 1 0 5 5 5 5 0 0 0-5-5zm0 8a3 3 0 1 1 3-3 3 3 0 0 1-3 3zm9 11v-1a7 7 0 0 0-7-7h-4a7 7 0 0 0-7 7v1h2v-1a5 5 0 0 1 5-5h4a5 5 0 0 1 5 5v1z',
31
+ // Boxicons bx-buildings
32
+ pages: 'M19 2H9c-1.103 0-2 .897-2 2v6H5c-1.103 0-2 .897-2 2v9a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V4c0-1.103-.897-2-2-2zM5 12h6v8H5v-8zm14 8h-6v-8c0-1.103-.897-2-2-2H9V4h10v16zM11 6h2v2h-2zm4 0h2v2h-2zm0 4.031h2V12h-2zM15 14h2v2h-2zm-8 .001h2v2H7z',
33
+ };
27
34
  /** Flung's mark, from https://assets.flung.dev/brand/logo/flung-mark-on-light.svg. */
28
- export const FLUNG_MARK = { viewBox: '0 0 500 500', path: 'M 189.525 16.874 C 190.808 16.873 192.091 16.873 193.413 16.872 C 196.941 16.869 200.467 16.873 203.992 16.878 C 207.81 16.884 211.625 16.881 215.442 16.881 C 222.042 16.88 228.642 16.884 235.242 16.891 C 244.784 16.899 254.326 16.902 263.868 16.904 C 279.352 16.906 294.837 16.913 310.322 16.924 C 325.357 16.933 340.393 16.941 355.428 16.947 C 356.82 16.947 356.82 16.947 358.24 16.947 C 362.896 16.949 367.55 16.95 372.206 16.951 C 410.783 16.963 449.359 16.982 487.935 17.007 C 487.954 19.653 487.967 22.299 487.978 24.945 C 487.986 26.06 487.986 26.06 487.994 27.198 C 488.011 33.049 487.504 38.516 486.278 44.226 C 485.989 45.603 485.989 45.603 485.694 47.009 C 479.753 73.338 467.128 96.459 449.249 116.488 C 448.329 117.521 447.408 118.554 446.46 119.619 C 434.313 132.43 419.889 141.304 403.93 148.543 C 402.995 148.995 402.061 149.447 401.097 149.912 C 387.969 155.816 373.086 157.455 358.886 158.492 C 337.903 160.08 318.12 166.235 302.238 180.598 C 301.228 181.476 301.228 181.476 300.196 182.373 C 290.186 191.634 284.215 203.986 280.409 216.88 C 276.039 231.615 269.265 243.914 255.779 252.324 C 245.106 257.877 235.96 259.309 224.105 259.493 C 207.259 259.997 192.415 263.213 180.305 275.865 C 171.548 286.39 167.399 299.632 168.491 313.239 C 169.76 324.473 175.361 336.705 183.966 344.189 C 197.7 354.156 210.093 357.822 227.074 356.348 C 240.814 353.699 252.153 345.546 260.235 334.242 C 264.938 325.96 267.358 317.705 268.395 308.295 C 270.357 290.773 276.223 278.048 289.803 266.539 C 299.876 258.871 311.257 253.264 324.085 253.285 C 324.795 253.283 325.507 253.282 326.238 253.28 C 328.597 253.276 330.956 253.287 333.315 253.3 C 335.015 253.301 336.714 253.301 338.415 253.3 C 343.014 253.298 347.612 253.311 352.211 253.327 C 357.024 253.34 361.836 253.341 366.649 253.344 C 375.754 253.351 384.859 253.369 393.963 253.391 C 404.333 253.416 414.703 253.429 425.071 253.439 C 446.395 253.463 467.718 253.502 489.041 253.551 C 489.12 263.833 489.178 274.116 489.213 284.399 C 489.231 289.177 489.253 293.955 489.292 298.732 C 489.713 353.119 479.51 398.095 440.406 438.144 C 402.556 475.574 354.507 484.025 303.516 483.908 C 298.647 483.9 293.777 483.908 288.908 483.913 C 280.739 483.918 272.57 483.91 264.401 483.895 C 255.01 483.876 245.619 483.883 236.228 483.901 C 228.105 483.916 219.983 483.918 211.86 483.909 C 207.034 483.904 202.21 483.903 197.384 483.914 C 159.131 483.99 159.131 483.99 143.068 480.146 C 141.987 479.903 140.905 479.66 139.79 479.408 C 137.277 478.822 134.828 478.14 132.36 477.383 C 131.465 477.109 130.569 476.835 129.646 476.552 C 105.884 468.875 84.897 456.337 66.8 439.249 C 65.448 438.112 64.09 436.983 62.724 435.863 C 32.664 410.058 16.633 368.22 12.873 329.721 C 12.432 323.684 12.465 317.647 12.46 311.597 C 12.454 310.143 12.448 308.689 12.443 307.192 C 12.428 303.231 12.422 299.269 12.416 295.309 C 12.413 292.822 12.409 290.335 12.405 287.848 C 12.389 280.032 12.378 272.215 12.374 264.399 C 12.369 255.444 12.35 246.487 12.318 237.531 C 12.294 230.566 12.282 223.6 12.28 216.633 C 12.28 212.495 12.274 208.355 12.254 204.215 C 12.154 182.613 12.574 161.364 18.717 140.46 C 18.991 139.523 19.265 138.584 19.548 137.619 C 27.777 111.087 42.745 89.246 61.273 68.959 C 62.004 68.134 62.735 67.311 63.487 66.463 C 95.594 32.13 143.615 16.748 189.525 16.874 Z' };
35
+ export const FLUNG_MARK = { viewBox: '-2 -2 36 36', path: 'M12.942 6.235L21.674 4.058A6.0 6.0 0 0 1 28.947 8.428L29.915 12.309A6.0 6.0 0 0 1 25.545 19.582L16.812 21.76A6.0 6.0 0 0 1 9.539 17.39L8.571 13.508A6.0 6.0 0 0 1 12.942 6.235ZM13.758 19.429L20.55 17.736L14.528 29.131Z M3 12.2H7.4 M2.2 18H5.4' };
@@ -1,6 +1,8 @@
1
+ /** What `unstyled` keeps: the element is a block, and `hidden` hides it. Everything else is the page's, through `::part()`. */
2
+ export declare const BASE_STYLES = "\n:host { display: block; }\n:host([hidden]) { display: none; }\n[hidden] { display: none !important; }\n";
1
3
  /**
2
- * The panel's look, inside its shadow root. Every colour, the font and the corner radius are `--flung-*` properties a
3
- * page can set on the element; each part has a `part` name for `::part()`. Follows the page's light or dark scheme
4
- * unless `theme` says which.
4
+ * The panel's look, inside its shadow root. Colours, type, spacing, sizes, radii, borders, shadows and the focus ring
5
+ * are `--flung-*` properties a page can set on the element; each piece has a `part` name for `::part()`. Follows the
6
+ * page's light or dark scheme unless `theme` says which. `layout` picks `list`, `grid` or `compact`.
5
7
  */
6
8
  export declare const STYLES: string;
@@ -1,9 +1,27 @@
1
1
  import { PLATFORMS } from '../core/types.js';
2
2
  import { PLATFORM_MARKS } from './marks.js';
3
- /** Each platform's brand colour for its mark. A black mark takes the text colour, so it shows on a dark page too. */
4
- const MARK_COLOURS = PLATFORMS.map((platform) => {
3
+ /**
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) => {
5
23
  const hex = PLATFORM_MARKS[platform].hex;
6
- return `.platform[data-platform="${platform}"] { --mark: ${hex === '000000' ? 'var(--flung-text)' : `#${hex}`}; }`;
24
+ return `.platform[data-platform="${platform}"] { --mark: ${hex === '000000' ? 'var(--flung-text)' : `#${hex}`}; --mark-scale: ${OPTICAL_SCALE[platform]}; }`;
7
25
  }).join('\n');
8
26
  const DARK = `
9
27
  --flung-text: #f4f4f5;
@@ -16,15 +34,27 @@ const DARK = `
16
34
  --flung-danger: #f87171;
17
35
  --flung-warning: #fbbf24;
18
36
  --flung-success: #4ade80;
19
- --flung-mark-strength: 70%;`;
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
+ `;
20
45
  /**
21
- * The panel's look, inside its shadow root. Every colour, the font and the corner radius are `--flung-*` properties a
22
- * page can set on the element; each part has a `part` name for `::part()`. Follows the page's light or dark scheme
23
- * unless `theme` says which.
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`.
24
49
  */
25
50
  export const STYLES = `
26
51
  :host {
27
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;
28
58
  --flung-text: #18181b;
29
59
  --flung-muted: #71717a;
30
60
  --flung-surface: #ffffff;
@@ -36,74 +66,170 @@ export const STYLES = `
36
66
  --flung-warning: #b45309;
37
67
  --flung-success: #15803d;
38
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;
39
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;
87
+ --flung-qr-size: 132px;
40
88
  display: block;
41
- font: 15px/1.45 var(--flung-font);
89
+ font: var(--flung-font-size)/var(--flung-line-height) var(--flung-font);
42
90
  color: var(--flung-text);
43
91
  -webkit-font-smoothing: antialiased;
44
92
  }
45
93
  :host([hidden]) { display: none; }
94
+ [hidden] { display: none !important; }
46
95
  @media (prefers-color-scheme: dark) {
47
96
  :host(:not([theme="light"])) {${DARK}
48
97
  }
49
98
  }
50
99
  :host([theme="dark"]) {${DARK}
51
100
  }
101
+ :host([layout="compact"]) {
102
+ --flung-font-size: 14px;
103
+ --flung-font-size-small: 12px;
104
+ --flung-padding: 12px;
105
+ --flung-gap: 8px;
106
+ --flung-badge-size: 30px;
107
+ --flung-mark-size: 16px;
108
+ --flung-avatar-size: 24px;
109
+ --flung-button-height: 30px;
110
+ --flung-button-padding: 0 10px;
111
+ }
52
112
  * { box-sizing: border-box; }
53
- .panel { background: var(--flung-surface); border: 1px solid var(--flung-border); border-radius: var(--flung-radius); overflow: hidden; }
113
+ .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; }
54
114
  ul { list-style: none; margin: 0; padding: 0; }
55
- .platform { padding: 12px 16px; border-top: 1px solid var(--flung-border); }
115
+ .platform { padding: var(--flung-gap) var(--flung-padding); border-top: var(--flung-border-width) solid var(--flung-border); }
56
116
  .platform:first-child { border-top: 0; }
57
- .row { display: flex; align-items: center; gap: 12px; min-height: 40px; }
58
- .badge { flex: none; width: 40px; height: 40px; border-radius: calc(var(--flung-radius) - 2px); display: grid; place-items: center; color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text)); background: color-mix(in srgb, var(--mark, var(--flung-text)) 10%, var(--flung-surface)); }
59
- .mark { width: 20px; height: 20px; fill: currentColor; display: block; }
117
+ .row { display: flex; align-items: center; gap: var(--flung-gap); min-height: var(--flung-badge-size); }
118
+ .row ::slotted(*) { flex: 1; min-width: 0; }
119
+ .badge {
120
+ flex: none; width: var(--flung-badge-size); height: var(--flung-badge-size); border-radius: var(--flung-radius-inner);
121
+ display: grid; place-items: center;
122
+ color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
123
+ background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
124
+ border: var(--flung-border-width) solid color-mix(in srgb, var(--mark, var(--flung-text)) 14%, var(--flung-border));
125
+ }
126
+ .mark { width: var(--flung-mark-size); height: var(--flung-mark-size); fill: currentColor; display: block; transform: scale(var(--mark-scale, 1)); }
60
127
  .title { flex: 1; min-width: 0; display: flex; flex-direction: column; }
61
- .name { font-weight: 600; line-height: 1.25; }
62
- .count { font-size: 13px; color: var(--flung-muted); line-height: 1.3; }
128
+ .name { font-weight: var(--flung-font-weight-strong); line-height: 1.25; }
129
+ .count { font-size: var(--flung-font-size-small); color: var(--flung-muted); line-height: 1.3; }
63
130
  .count.on::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--flung-success); margin-right: 6px; vertical-align: 2px; }
64
- .accounts { margin: 10px 0 2px 52px; border-left: 1px solid var(--flung-border); }
65
- .account { display: flex; align-items: center; gap: 8px; padding: 6px 0 6px 12px; min-height: 36px; }
131
+ .accounts { margin: 10px 0 2px calc(var(--flung-badge-size) + var(--flung-gap)); border-left: var(--flung-border-width) solid var(--flung-border); }
132
+ .account { display: flex; align-items: center; gap: 10px; padding: 6px 0 6px 12px; min-height: calc(var(--flung-avatar-size) + 12px); }
133
+ .account ::slotted(*) { flex: 1; min-width: 0; }
134
+ .avatar {
135
+ flex: none; width: var(--flung-avatar-size); height: var(--flung-avatar-size); border-radius: 50%; object-fit: cover; display: block;
136
+ background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
137
+ border: var(--flung-border-width) solid color-mix(in srgb, var(--mark, var(--flung-text)) 14%, var(--flung-border));
138
+ }
139
+ .avatar.initial {
140
+ display: grid; place-items: center; font-size: calc(var(--flung-avatar-size) * .42); font-weight: var(--flung-font-weight-strong); line-height: 1;
141
+ color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
142
+ }
66
143
  .who { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 2px; }
67
- .handle { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
68
- .status { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; 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; }
69
- .actions { flex: none; display: flex; gap: 4px; }
70
- .note { margin: 10px 0 0 52px; color: var(--flung-muted); font-size: 14px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
144
+ .names { min-width: 0; max-width: 100%; display: flex; flex-direction: column; }
145
+ .handle, .username { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
146
+ .handle { font-size: calc(var(--flung-font-size) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1.3; }
147
+ .username { font-size: var(--flung-font-size-small); color: var(--flung-muted); line-height: 1.3; }
148
+ .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; }
149
+ .actions { flex: none; display: flex; align-items: center; gap: 4px; }
150
+ .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; }
151
+ .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; }
71
152
  .note-text { flex: 1 1 12rem; min-width: 0; }
72
- .note a { color: var(--flung-text); font-weight: 500; }
153
+ .note a { color: var(--flung-text); font-weight: var(--flung-font-weight-medium); }
73
154
  .spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--flung-border); border-top-color: var(--flung-text); flex: none; }
74
- .confirm, .form { margin: 10px 0 0 52px; padding: 12px; border-radius: calc(var(--flung-radius) - 2px); background: var(--flung-raised); display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
75
- .confirm p { flex-basis: 100%; margin: 0 0 2px; font-size: 14px; }
76
- .form label { flex-basis: 100%; font-size: 13px; color: var(--flung-muted); }
77
- .form input { flex: 1 1 12rem; min-width: 0; font: inherit; font-size: 14px; color: inherit; background: var(--flung-surface); border: 1px solid var(--flung-border); border-radius: calc(var(--flung-radius) - 4px); padding: 7px 10px; }
78
- button { font: inherit; font-size: 14px; font-weight: 500; line-height: 1.2; cursor: pointer; border-radius: calc(var(--flung-radius) - 4px); padding: 8px 14px; border: 1px solid var(--flung-border); background: var(--flung-surface); color: inherit; white-space: nowrap; }
155
+ .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; }
156
+ .confirm p { flex-basis: 100%; margin: 0 0 2px; font-size: calc(var(--flung-font-size) - 1px); }
157
+ .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; }
158
+ .choose-title { margin: 0; font-size: calc(var(--flung-font-size) - 1px); }
159
+ .options { align-self: stretch; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 8px; }
160
+ 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); }
161
+ button.option:hover:not(:disabled) { background: var(--flung-surface); border-color: color-mix(in srgb, var(--mark, var(--flung-text)) 45%, var(--flung-border)); }
162
+ .option-icon {
163
+ flex: none; width: 32px; height: 32px; border-radius: var(--flung-radius-inner); display: grid; place-items: center;
164
+ color: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-mark-strength), var(--flung-text));
165
+ background: color-mix(in srgb, var(--mark, var(--flung-text)) var(--flung-tint-strength), var(--flung-surface));
166
+ }
167
+ .option-mark { width: 18px; height: 18px; fill: currentColor; display: block; }
168
+ .option-text { min-width: 0; display: flex; flex-direction: column; }
169
+ .option-name { font-weight: var(--flung-font-weight-strong); line-height: 1.3; }
170
+ .option-description { font-size: var(--flung-font-size-small); font-weight: normal; color: var(--flung-muted); line-height: 1.3; }
171
+ .approve { 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; gap: 10px; }
172
+ .approve-text { margin: 0; font-size: calc(var(--flung-font-size) - 1px); }
173
+ .approve.ended .approve-text { color: var(--flung-danger); }
174
+ .approve-actions { display: flex; flex-wrap: wrap; gap: 8px; }
175
+ .approve-go { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
176
+ .approve-open {
177
+ display: inline-flex; align-items: center; height: var(--flung-button-height); padding: var(--flung-button-padding); border-radius: var(--flung-button-radius);
178
+ font-size: calc(var(--flung-font-size) - 1px); font-weight: var(--flung-font-weight-medium); text-decoration: none; white-space: nowrap;
179
+ background: var(--flung-accent); color: var(--flung-accent-text); border: var(--flung-border-width) solid var(--flung-accent);
180
+ }
181
+ .approve-open:hover { background: color-mix(in srgb, var(--flung-accent) 88%, var(--flung-surface)); }
182
+ .approve-scan { display: flex; align-items: center; gap: var(--flung-gap); }
183
+ .qr { flex: none; width: var(--flung-qr-size); height: var(--flung-qr-size); display: block; border-radius: var(--flung-radius-inner); border: var(--flung-border-width) solid var(--flung-border); }
184
+ .approve-hint { margin: 0; font-size: var(--flung-font-size-small); color: var(--flung-muted); max-width: 16rem; }
185
+ .approve .note { margin: 0; }
186
+ .form label { flex-basis: 100%; font-size: var(--flung-font-size-small); color: var(--flung-muted); }
187
+ .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; }
188
+ button {
189
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
190
+ height: var(--flung-button-height); padding: var(--flung-button-padding); border-radius: var(--flung-button-radius);
191
+ font: inherit; font-size: calc(var(--flung-font-size) - 1px); font-weight: var(--flung-font-weight-medium); line-height: 1; white-space: nowrap;
192
+ cursor: pointer; color: var(--flung-text); background: var(--flung-surface); border: var(--flung-border-width) solid var(--flung-border);
193
+ }
79
194
  button:hover:not(:disabled) { background: var(--flung-raised); }
195
+ button:active:not(:disabled) { background: color-mix(in srgb, var(--flung-text) 8%, var(--flung-raised)); }
80
196
  button.primary { background: var(--flung-accent); border-color: var(--flung-accent); color: var(--flung-accent-text); }
81
- button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-accent) 85%, var(--flung-surface)); }
82
- button.quiet { border-color: transparent; background: transparent; color: var(--flung-muted); padding: 6px 10px; font-size: 13px; }
197
+ button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-accent) 88%, var(--flung-surface)); }
198
+ button.primary:active:not(:disabled) { background: color-mix(in srgb, var(--flung-accent) 78%, var(--flung-surface)); }
199
+ button.quiet { border-color: transparent; background: transparent; color: var(--flung-muted); padding: 0 10px; }
83
200
  button.quiet:hover:not(:disabled) { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 8%, transparent); }
84
- button.warning { padding: 6px 10px; font-size: 13px; color: var(--flung-warning); border-color: color-mix(in srgb, var(--flung-warning) 40%, var(--flung-border)); }
201
+ button.quiet:active:not(:disabled) { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 14%, transparent); }
85
202
  button.danger { background: var(--flung-danger); border-color: var(--flung-danger); color: #ffffff; }
86
- button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-danger) 85%, #000000); }
203
+ button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--flung-danger) 88%, #000000); }
204
+ button.danger:active:not(:disabled) { background: color-mix(in srgb, var(--flung-danger) 78%, #000000); }
87
205
  button:disabled { opacity: .5; cursor: default; }
88
- button:focus-visible, input:focus-visible, a:focus-visible { outline: 2px solid var(--flung-accent); outline-offset: 2px; }
89
- .message { margin: 0; padding: 10px 16px; border-top: 1px solid var(--flung-border); color: var(--flung-muted); font-size: 14px; background: var(--flung-raised); }
90
- .message:empty { display: none; }
206
+ 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); }
207
+ .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); }
91
208
  .message.error { color: var(--flung-danger); background: color-mix(in srgb, var(--flung-danger) 8%, var(--flung-surface)); }
92
- .footer { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 16px; border-top: 1px solid var(--flung-border); font-size: 12px; color: var(--flung-muted); }
93
- .footer a { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; font-weight: 600; }
209
+ .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); }
210
+ .footer a { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; font-weight: var(--flung-font-weight-strong); }
94
211
  .footer a:hover { color: var(--flung-text); }
95
212
  .footer .flung { width: 14px; height: 14px; fill: currentColor; display: block; }
213
+ .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); }
214
+ .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); }
215
+ .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, .panel[data-layout="grid"] .approve { margin-left: 0; }
216
+ .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; }
217
+ .panel[data-layout="grid"] .account { padding-left: 0; }
218
+ .panel[data-layout="compact"] .count { display: none; }
219
+ .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; }
220
+ .panel[data-layout="compact"] .account { padding-top: 3px; padding-bottom: 3px; }
96
221
  @media (max-width: 420px) {
97
- .platform { padding: 12px; }
98
- .accounts, .note, .confirm, .form { margin-left: 0; }
99
- .accounts { border-left: 0; border-top: 1px solid var(--flung-border); margin-top: 10px; }
222
+ .platform { padding: var(--flung-gap); }
223
+ .accounts, .note, .confirm, .form, .choose, .approve { margin-left: 0; }
224
+ .approve-scan { display: none; }
225
+ .accounts { border-left: 0; border-top: var(--flung-border-width) solid var(--flung-border); margin-top: 10px; }
100
226
  .account { padding-left: 0; }
101
- .row > button { padding: 8px 12px; }
227
+ .row button { padding: 0 12px; }
102
228
  }
103
229
  @media (prefers-reduced-motion: no-preference) {
104
- button { transition: background-color .15s, color .15s, opacity .15s; }
230
+ button { transition: background-color .15s ease, color .15s ease, border-color .15s ease, opacity .15s ease; }
105
231
  .spinner { animation: flung-spin .8s linear infinite; }
106
232
  }
107
233
  @keyframes flung-spin { to { transform: rotate(360deg); } }
108
- ${MARK_COLOURS}
234
+ ${MARKS}
109
235
  `;
@@ -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 { DEFAULT_LABELS } from './element/copy.js';
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
@@ -4,5 +4,4 @@
4
4
  * or call `defineFlungConnect()`.
5
5
  */
6
6
  export { defineFlungConnect, TAG_NAME } from './element/define.js';
7
- export { DEFAULT_LABELS } from './element/copy.js';
8
7
  export * from './core/index.js';
@@ -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
@@ -20,6 +43,15 @@ export interface FlungConnectProps {
20
43
  mode?: 'popup' | 'redirect' | undefined;
21
44
  /** `light` or `dark`. Follows the page's colour scheme when left out. */
22
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;
23
55
  /** `none` hides the small "Secured by Flung" line under the list. */
24
56
  branding?: 'none' | undefined;
25
57
  /** Any of the panel's words, to put it in another language. */
@@ -28,6 +60,13 @@ export interface FlungConnectProps {
28
60
  className?: string | undefined;
29
61
  /** Styles for the element, such as its `--flung-*` custom properties. */
30
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;
31
70
  /** The accounts were read: on first show and after every change. */
32
71
  onLoad?: ((detail: LoadDetail) => void) | undefined;
33
72
  /** A connect or reconnect finished. */
@@ -37,4 +76,5 @@ export interface FlungConnectProps {
37
76
  /** A call or a connect failed. */
38
77
  onError?: ((detail: ErrorDetail) => void) | undefined;
39
78
  }
79
+ /** The `<flung-connect>` element as a React component. */
40
80
  export declare const FlungConnect: import("react").ForwardRefExoticComponent<FlungConnectProps & import("react").RefAttributes<FlungConnectElement>>;