@riebeckite/plugin-share 0.0.21 → 0.0.23
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 +26 -48
- package/README_ja.md +20 -41
- package/dist/components/share-controls.d.ts +12 -0
- package/dist/components/share-controls.js +47 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +72 -36
- package/dist/src/options.d.ts +1 -2
- package/dist/src/services.d.ts +1 -1
- package/dist/src/types.d.ts +0 -5
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
# @riebeckite/plugin-share
|
|
2
2
|
|
|
3
|
+
<!-- Generated from docs/docs/plugins/share.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
|
+
|
|
3
5
|
Per-article share controls built at build time. For every published entry the
|
|
4
|
-
plugin builds share URLs for the configured services and
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
plugin builds share URLs for the configured services and provides the control
|
|
7
|
+
data. The Site renders `<ShareControls />` itself, determining position in the
|
|
8
|
+
layout. The links are ordinary anchors and work without JavaScript; only the
|
|
9
|
+
copy-link action is a progressive enhancement.
|
|
7
10
|
|
|
8
11
|
[日本語](./README_ja.md)
|
|
9
12
|
|
|
10
13
|
## Overview
|
|
11
14
|
|
|
12
15
|
`share()` resolves each note's `permalink` to an absolute URL, composes the
|
|
13
|
-
service URLs from the note title and URL, and
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
service URLs from the note title and URL, and builds share link data. The data
|
|
17
|
+
is available via `getShareLinks({ entry, site, options? })` and rendered by the
|
|
18
|
+
Site using `<ShareControls links={...} />`. The controls are ordinary anchors
|
|
19
|
+
and work without JavaScript; only the copy-link action is a progressive
|
|
20
|
+
enhancement.
|
|
17
21
|
|
|
18
22
|
Supported services:
|
|
19
23
|
|
|
@@ -27,7 +31,7 @@ Supported services:
|
|
|
27
31
|
| `hatena` | `https://b.hatena.ne.jp/add?mode=confirm&url=…&title=…` |
|
|
28
32
|
| `copy` | Copies the note URL (button, no JavaScript required for the links) |
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
Mastodon is opt-in: add `"mastodon"` to `services` and set
|
|
31
35
|
`mastodonInstance`.
|
|
32
36
|
|
|
33
37
|
## Usage
|
|
@@ -42,22 +46,11 @@ export default defineConfig({
|
|
|
42
46
|
});
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
Enable Mastodon and change placement:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
share({
|
|
49
|
-
services: ["x", "bluesky", "mastodon", "copy"],
|
|
50
|
-
mastodonInstance: "mastodon.social",
|
|
51
|
-
placement: "top",
|
|
52
|
-
});
|
|
53
|
-
```
|
|
54
|
-
|
|
55
49
|
## Options
|
|
56
50
|
|
|
57
51
|
| Option | Type | Default | Description |
|
|
58
52
|
| ------ | ---- | ------- | ----------- |
|
|
59
53
|
| `services` | `ShareService[]` | every service except `mastodon` | Services to render, in order |
|
|
60
|
-
| `placement` | `"top" \| "bottom"` | `"bottom"` | Where the controls appear |
|
|
61
54
|
| `mastodonInstance` | `string` | none | Mastodon host; required when `services` includes `"mastodon"` |
|
|
62
55
|
| `className` | `string` | none | Extra root class alongside the stable `rr-share` hook |
|
|
63
56
|
| `ariaLabel` | `string` | `"Share"` | Accessible name for the control group |
|
|
@@ -68,35 +61,22 @@ share({
|
|
|
68
61
|
`share` throws a configuration error at `riebeckite check` time when
|
|
69
62
|
`services` includes `"mastodon"` but `mastodonInstance` is missing.
|
|
70
63
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
<a class="rr-share__link rr-share__link--x"
|
|
79
|
-
href="https://twitter.com/intent/tweet?url=…&text=…"
|
|
80
|
-
target="_blank" rel="noopener noreferrer" data-share-service="x">X</a>
|
|
81
|
-
</li>
|
|
82
|
-
<li class="rr-share__item">
|
|
83
|
-
<button type="button" class="rr-share__button rr-share__copy"
|
|
84
|
-
data-rr-share-copy data-share-url="https://example.com/posts/hello"
|
|
85
|
-
data-rr-share-copied="Copied" hidden>Copy link</button>
|
|
86
|
-
</li>
|
|
87
|
-
</ul>
|
|
88
|
-
<p class="rr-share__status" role="status" aria-live="polite"></p>
|
|
89
|
-
</div>
|
|
90
|
-
```
|
|
64
|
+
## Display
|
|
65
|
+
|
|
66
|
+
Share controls are rendered at the position determined by the Site, where
|
|
67
|
+
`<ShareControls links={...} />` is placed in the layout. On this site they
|
|
68
|
+
appear as live controls on the page, including the progressively enhanced
|
|
69
|
+
copy-link action. The Site decides whether to render controls at the top or
|
|
70
|
+
bottom of the article, or at some other position.
|
|
91
71
|
|
|
92
|
-
|
|
72
|
+
Progressive enhancement:
|
|
93
73
|
|
|
94
74
|
The share links are plain anchors, so they work with JavaScript disabled. The
|
|
95
75
|
copy-link button starts `hidden` and is revealed by `initShare()`, which the
|
|
96
76
|
app calls during page initialization; a no-JS reader never sees a control that
|
|
97
77
|
cannot work. Copying uses `navigator.clipboard.writeText` and falls back to a
|
|
98
|
-
hidden textarea with `document.execCommand("copy")`. The result is announced
|
|
99
|
-
the `aria-live="polite"` status region.
|
|
78
|
+
hidden textarea with `document.execCommand("copy")`. The result is announced
|
|
79
|
+
in the `aria-live="polite"` status region.
|
|
100
80
|
|
|
101
81
|
## Style
|
|
102
82
|
|
|
@@ -108,8 +88,8 @@ import "@riebeckite/plugin-share/style.css";
|
|
|
108
88
|
|
|
109
89
|
The stable classes are `rr-share` (root), `rr-share__list`, `rr-share__item`,
|
|
110
90
|
`rr-share__link`, `rr-share__link--{service}`, `rr-share__button`,
|
|
111
|
-
`rr-share__copy`, and `rr-share__status`. A theme can target these hooks;
|
|
112
|
-
optional `className` is added to the root without replacing them.
|
|
91
|
+
`rr-share__copy`, and `rr-share__status`. A theme can target these hooks;
|
|
92
|
+
the optional `className` is added to the root without replacing them.
|
|
113
93
|
|
|
114
94
|
## Exports
|
|
115
95
|
|
|
@@ -127,15 +107,13 @@ optional `className` is added to the root without replacing them.
|
|
|
127
107
|
- Constants: `SHARE_SERVICES`, `SHARE_ATTRIBUTE`, `SHARE_ROOT_CLASS`,
|
|
128
108
|
`DEFAULT_SHARE_SERVICES`, `DEFAULT_SHARE_LABELS`, `DEFAULT_SHARE_PLACEMENT`
|
|
129
109
|
- Types: `ShareOptions`, `ResolvedShareOptions`, `ShareService`,
|
|
130
|
-
`
|
|
110
|
+
`ShareLink`, `ShareTarget`
|
|
131
111
|
|
|
132
112
|
## Limitations
|
|
133
113
|
|
|
134
114
|
- Share URLs are fixed at build time. A full rebuild always recomputes them.
|
|
135
115
|
- Mastodon cannot be enabled without a concrete instance.
|
|
136
|
-
- The plugin contributes controls to `article.before-content` for `top` and
|
|
137
|
-
`article.footer` for `bottom`; a Site places those semantic slots in its layout.
|
|
138
116
|
|
|
139
117
|
## See also
|
|
140
118
|
|
|
141
|
-
- [Plugin guide](
|
|
119
|
+
- [Plugin guide](https://github.com/Rerurate514/riebeckite/blob/main/docs/docs/reference/plugin-api.md)
|
package/README_ja.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# @riebeckite/plugin-share
|
|
2
2
|
|
|
3
|
+
<!-- Generated from docs/docs/plugins/share.ja.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
|
+
|
|
3
5
|
記事ごとの共有ボタンをビルド時に生成するプラグインです。公開対象の各エントリ
|
|
4
|
-
について、設定したサービス向けの共有 URL
|
|
5
|
-
|
|
6
|
+
について、設定したサービス向けの共有 URL を組み立て、Share controls のデータ
|
|
7
|
+
を提供します。Site が `<ShareControls links={...} />` を描画する位置をlayout で
|
|
8
|
+
決定します。共有リンクは通常の `<a>` なので JavaScript なしでも動作し、コピー
|
|
6
9
|
操作だけをプログレッシブ・エンハンスメントとして追加します。
|
|
7
10
|
|
|
8
11
|
[English](./README.md)
|
|
@@ -10,9 +13,11 @@
|
|
|
10
13
|
## 仕組み
|
|
11
14
|
|
|
12
15
|
`share()` は各ノートの `permalink` を絶対 URL に解決し、ノートのタイトルと URL
|
|
13
|
-
からサービスごとの共有 URL
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
からサービスごとの共有 URL を組み立て、Share control data を組み立てます。
|
|
17
|
+
データは `getShareLinks({ entry, site, options? })` で利用可能で、Site が
|
|
18
|
+
`<ShareControls links={...} />` で描画します。共有リンクは通常の `<a>` なので
|
|
19
|
+
JavaScript なしでも動作し、コピー操作だけをプログレッシブ・エンハンスメント
|
|
20
|
+
として追加します。
|
|
16
21
|
|
|
17
22
|
対応サービスは次のとおりです。
|
|
18
23
|
|
|
@@ -26,7 +31,7 @@ HTML と同期するため、生成ページとフィードの両方に反映さ
|
|
|
26
31
|
| `hatena` | `https://b.hatena.ne.jp/add?mode=confirm&url=…&title=…` |
|
|
27
32
|
| `copy` | ノートの URL をコピーするボタン(リンクは JS 不要) |
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
Mastodon はオプトインです。`services` に `"mastodon"` を追加し、
|
|
30
35
|
`mastodonInstance` を設定してください。
|
|
31
36
|
|
|
32
37
|
## 使い方
|
|
@@ -41,22 +46,11 @@ export default defineConfig({
|
|
|
41
46
|
});
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
Mastodon を有効にし、表示位置を変える例です。
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
share({
|
|
48
|
-
services: ["x", "bluesky", "mastodon", "copy"],
|
|
49
|
-
mastodonInstance: "mastodon.social",
|
|
50
|
-
placement: "top",
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
49
|
## オプション
|
|
55
50
|
|
|
56
51
|
| オプション | 型 | 既定値 | 説明 |
|
|
57
52
|
| ---------- | -- | ------ | ---- |
|
|
58
53
|
| `services` | `ShareService[]` | `mastodon` 以外のすべて | 表示するサービス(順序どおり) |
|
|
59
|
-
| `placement` | `"top" \| "bottom"` | `"bottom"` | コントロールの表示位置 |
|
|
60
54
|
| `mastodonInstance` | `string` | なし | Mastodon のホスト。`services` に `"mastodon"` を含めると必須 |
|
|
61
55
|
| `className` | `string` | なし | 安定した `rr-share` フックに追加するルートクラス |
|
|
62
56
|
| `ariaLabel` | `string` | `"Share"` | コントロールグループのアクセシブル名 |
|
|
@@ -67,26 +61,13 @@ share({
|
|
|
67
61
|
`services` に `"mastodon"` を含めたのに `mastodonInstance` が未設定の場合、
|
|
68
62
|
`riebeckite check` の時点で設定エラーになります。
|
|
69
63
|
|
|
70
|
-
##
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<a class="rr-share__link rr-share__link--x"
|
|
78
|
-
href="https://twitter.com/intent/tweet?url=…&text=…"
|
|
79
|
-
target="_blank" rel="noopener noreferrer" data-share-service="x">X</a>
|
|
80
|
-
</li>
|
|
81
|
-
<li class="rr-share__item">
|
|
82
|
-
<button type="button" class="rr-share__button rr-share__copy"
|
|
83
|
-
data-rr-share-copy data-share-url="https://example.com/posts/hello"
|
|
84
|
-
data-rr-share-copied="Copied" hidden>Copy link</button>
|
|
85
|
-
</li>
|
|
86
|
-
</ul>
|
|
87
|
-
<p class="rr-share__status" role="status" aria-live="polite"></p>
|
|
88
|
-
</div>
|
|
89
|
-
```
|
|
64
|
+
## 表示
|
|
65
|
+
|
|
66
|
+
共有コントロールは、Site が layout で `<ShareControls links={...} />` を
|
|
67
|
+
描画する位置に実際に描画されます。このサイトでは、プログレッシブ・エンハンス
|
|
68
|
+
メントされたリンクコピー操作を含むコントロールとして表示します。Site は
|
|
69
|
+
コントロールを記事のトップやボトム、またはその他の位置に描画するかどうかを
|
|
70
|
+
決定します。
|
|
90
71
|
|
|
91
72
|
## プログレッシブ・エンハンスメント
|
|
92
73
|
|
|
@@ -127,15 +108,13 @@ import "@riebeckite/plugin-share/style.css";
|
|
|
127
108
|
- 定数: `SHARE_SERVICES`, `SHARE_ATTRIBUTE`, `SHARE_ROOT_CLASS`,
|
|
128
109
|
`DEFAULT_SHARE_SERVICES`, `DEFAULT_SHARE_LABELS`, `DEFAULT_SHARE_PLACEMENT`
|
|
129
110
|
- 型: `ShareOptions`, `ResolvedShareOptions`, `ShareService`,
|
|
130
|
-
`
|
|
111
|
+
`ShareLink`, `ShareTarget`
|
|
131
112
|
|
|
132
113
|
## 制約
|
|
133
114
|
|
|
134
115
|
- 共有 URL はビルド時に確定します。再ビルドすれば常に正しく再計算されます。
|
|
135
116
|
- Mastodon は具体的なインスタンスなしには有効化できません。
|
|
136
|
-
- `top` は `article.before-content`、`bottom` は `article.footer` にコントロールを
|
|
137
|
-
提供します。Site は layout でその semantic slot を配置します。
|
|
138
117
|
|
|
139
118
|
## 関連
|
|
140
119
|
|
|
141
|
-
- [プラグインガイド](
|
|
120
|
+
- [プラグインガイド](https://github.com/Rerurate514/riebeckite/blob/main/docs/docs/reference/plugin-api.ja.md)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ShareLink } from "../src/types.js";
|
|
2
|
+
export type ShareControlsProps = {
|
|
3
|
+
readonly links: readonly ShareLink[];
|
|
4
|
+
readonly copyUrl?: string;
|
|
5
|
+
readonly ariaLabel?: string;
|
|
6
|
+
readonly className?: string;
|
|
7
|
+
readonly copyLabel?: string;
|
|
8
|
+
readonly copiedLabel?: string;
|
|
9
|
+
readonly copyFailedLabel?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const ShareControls: ({ links, copyUrl, ariaLabel, className, copyLabel, copiedLabel, copyFailedLabel, }: Readonly<ShareControlsProps>) => import("hono/jsx/jsx-dev-runtime").JSX.Element;
|
|
12
|
+
export default ShareControls;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// components/share-controls.tsx
|
|
2
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
3
|
+
var ShareControls = (({
|
|
4
|
+
links,
|
|
5
|
+
copyUrl,
|
|
6
|
+
ariaLabel = "Share",
|
|
7
|
+
className = "rr-share",
|
|
8
|
+
copyLabel = "Copy link",
|
|
9
|
+
copiedLabel = "Copied",
|
|
10
|
+
copyFailedLabel = "Copy failed"
|
|
11
|
+
}) => {
|
|
12
|
+
if (links.length === 0 && !copyUrl) return null;
|
|
13
|
+
const rootClass = className === "rr-share" ? className : `rr-share ${className}`;
|
|
14
|
+
return /* @__PURE__ */ jsxs("nav", { class: rootClass, "data-rr-share": true, "aria-label": ariaLabel, children: [
|
|
15
|
+
/* @__PURE__ */ jsxs("ul", { class: "rr-share__list", children: [
|
|
16
|
+
links.map((link) => /* @__PURE__ */ jsx("li", { class: "rr-share__item", children: /* @__PURE__ */ jsx(
|
|
17
|
+
"a",
|
|
18
|
+
{
|
|
19
|
+
class: `rr-share__link rr-share__link--${link.service}`,
|
|
20
|
+
href: link.url,
|
|
21
|
+
target: "_blank",
|
|
22
|
+
rel: "noopener noreferrer",
|
|
23
|
+
"data-share-service": link.service,
|
|
24
|
+
children: link.label
|
|
25
|
+
}
|
|
26
|
+
) }, link.service)),
|
|
27
|
+
copyUrl ? /* @__PURE__ */ jsx("li", { class: "rr-share__item", children: /* @__PURE__ */ jsx(
|
|
28
|
+
"button",
|
|
29
|
+
{
|
|
30
|
+
type: "button",
|
|
31
|
+
class: "rr-share__button rr-share__copy",
|
|
32
|
+
"data-rr-share-copy": true,
|
|
33
|
+
"data-share-url": copyUrl,
|
|
34
|
+
"data-rr-share-copied": copiedLabel,
|
|
35
|
+
"data-rr-share-copy-failed": copyFailedLabel,
|
|
36
|
+
hidden: true,
|
|
37
|
+
children: copyLabel
|
|
38
|
+
}
|
|
39
|
+
) }) : null
|
|
40
|
+
] }),
|
|
41
|
+
/* @__PURE__ */ jsx("p", { class: "rr-share__status", role: "status", "aria-live": "polite" })
|
|
42
|
+
] });
|
|
43
|
+
});
|
|
44
|
+
var share_controls_default = ShareControls;
|
|
45
|
+
export {
|
|
46
|
+
share_controls_default as default
|
|
47
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
import { type PluginDataHelper, type PublicContentEntry, type ResolvedRiebeckiteConfig } from "@riebeckite/core";
|
|
1
2
|
import type { ShareOptions } from "./src/types.js";
|
|
3
|
+
export type { ShareControlsProps } from "./components/share-controls.js";
|
|
4
|
+
export { default as ShareControls } from "./components/share-controls.js";
|
|
2
5
|
export { initShare } from "./src/client.js";
|
|
3
|
-
export { DEFAULT_SHARE_ARIA_LABEL, DEFAULT_SHARE_COPIED_LABEL, DEFAULT_SHARE_COPY_FAILED_LABEL, DEFAULT_SHARE_LABELS,
|
|
6
|
+
export { DEFAULT_SHARE_ARIA_LABEL, DEFAULT_SHARE_COPIED_LABEL, DEFAULT_SHARE_COPY_FAILED_LABEL, DEFAULT_SHARE_LABELS, DEFAULT_SHARE_SERVICES, resolveShareOptions, validateShareOptions, } from "./src/options.js";
|
|
4
7
|
export { renderShareControls, SHARE_ATTRIBUTE, SHARE_ROOT_CLASS, } from "./src/render.js";
|
|
5
8
|
export { buildAbsoluteUrl, buildShareLinks, buildShareUrl, normalizeMastodonInstance, type ShareTarget, } from "./src/services.js";
|
|
6
|
-
export type { ResolvedShareOptions, ShareLink, ShareOptions,
|
|
9
|
+
export type { ResolvedShareOptions, ShareLink, ShareOptions, ShareService, } from "./src/types.js";
|
|
7
10
|
export { SHARE_SERVICES } from "./src/types.js";
|
|
11
|
+
export type ShareLinksData = {
|
|
12
|
+
readonly links: readonly import("./src/types.js").ShareLink[];
|
|
13
|
+
readonly copyUrl?: string;
|
|
14
|
+
readonly ariaLabel: string;
|
|
15
|
+
readonly className: string;
|
|
16
|
+
readonly copyLabel: string;
|
|
17
|
+
readonly copiedLabel: string;
|
|
18
|
+
readonly copyFailedLabel: string;
|
|
19
|
+
};
|
|
20
|
+
export declare const getShareLinks: PluginDataHelper<{
|
|
21
|
+
entry: PublicContentEntry;
|
|
22
|
+
site: Pick<ResolvedRiebeckiteConfig, "site">;
|
|
23
|
+
options?: ShareOptions;
|
|
24
|
+
}, ShareLinksData>;
|
|
8
25
|
export declare const SHARE_PLUGIN_NAME = "share";
|
|
9
26
|
/**
|
|
10
27
|
* Per-article share controls.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import {
|
|
3
|
-
appendContentBodySlot,
|
|
4
3
|
createClientEntry,
|
|
5
4
|
createStyleAsset,
|
|
6
5
|
definePlugin
|
|
@@ -89,14 +88,12 @@ var DEFAULT_SHARE_LABELS = {
|
|
|
89
88
|
hatena: "Hatena Bookmark",
|
|
90
89
|
copy: "Copy link"
|
|
91
90
|
};
|
|
92
|
-
var DEFAULT_SHARE_PLACEMENT = "bottom";
|
|
93
91
|
var DEFAULT_SHARE_ARIA_LABEL = "Share";
|
|
94
92
|
var DEFAULT_SHARE_COPIED_LABEL = "Copied";
|
|
95
93
|
var DEFAULT_SHARE_COPY_FAILED_LABEL = "Copy failed";
|
|
96
94
|
function resolveShareOptions(options = {}) {
|
|
97
95
|
return {
|
|
98
96
|
services: normalizeServices(options.services),
|
|
99
|
-
placement: options.placement === "top" ? "top" : DEFAULT_SHARE_PLACEMENT,
|
|
100
97
|
mastodonInstance: normalizeMastodonInstance(options.mastodonInstance),
|
|
101
98
|
className: normalizeNonEmpty(options.className, ""),
|
|
102
99
|
ariaLabel: normalizeNonEmpty(options.ariaLabel, DEFAULT_SHARE_ARIA_LABEL),
|
|
@@ -145,12 +142,6 @@ function validateShareOptions(options) {
|
|
|
145
142
|
message: "Expected a non-empty string."
|
|
146
143
|
});
|
|
147
144
|
}
|
|
148
|
-
if (options.placement !== void 0 && options.placement !== "top" && options.placement !== "bottom") {
|
|
149
|
-
issues.push({
|
|
150
|
-
path: "placement",
|
|
151
|
-
message: 'Expected "top" or "bottom".'
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
145
|
for (const key of [
|
|
155
146
|
"className",
|
|
156
147
|
"ariaLabel",
|
|
@@ -227,7 +218,7 @@ function renderShareControls(options, target) {
|
|
|
227
218
|
const items = links.map(renderLinkItem).join("");
|
|
228
219
|
const copyItem = wantsCopy ? renderCopyItem(options, target) : "";
|
|
229
220
|
const ariaLabel = escapeHtmlAttribute(options.ariaLabel);
|
|
230
|
-
return `<div class="${escapeHtmlAttribute(rootClass)}" ${SHARE_ATTRIBUTE}
|
|
221
|
+
return `<div class="${escapeHtmlAttribute(rootClass)}" ${SHARE_ATTRIBUTE} role="group" aria-label="${ariaLabel}"><ul class="${SHARE_ROOT_CLASS}__list">${items}${copyItem}</ul><p class="${SHARE_ROOT_CLASS}__status" role="status" aria-live="polite"></p></div>`;
|
|
231
222
|
}
|
|
232
223
|
function renderLinkItem(link) {
|
|
233
224
|
const modifier = `${SHARE_ROOT_CLASS}__link--${link.service}`;
|
|
@@ -237,6 +228,51 @@ function renderCopyItem(options, target) {
|
|
|
237
228
|
return `<li class="${SHARE_ROOT_CLASS}__item"><button type="button" class="${SHARE_ROOT_CLASS}__button ${SHARE_ROOT_CLASS}__copy" data-rr-share-copy data-share-url="${escapeHtmlAttribute(target.url)}" data-rr-share-copied="${escapeHtmlAttribute(options.copiedLabel)}" data-rr-share-copy-failed="${escapeHtmlAttribute(options.copyFailedLabel)}" hidden>${escapeHtml(options.labels.copy)}</button></li>`;
|
|
238
229
|
}
|
|
239
230
|
|
|
231
|
+
// components/share-controls.tsx
|
|
232
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
233
|
+
var ShareControls = (({
|
|
234
|
+
links,
|
|
235
|
+
copyUrl,
|
|
236
|
+
ariaLabel = "Share",
|
|
237
|
+
className = "rr-share",
|
|
238
|
+
copyLabel = "Copy link",
|
|
239
|
+
copiedLabel = "Copied",
|
|
240
|
+
copyFailedLabel = "Copy failed"
|
|
241
|
+
}) => {
|
|
242
|
+
if (links.length === 0 && !copyUrl) return null;
|
|
243
|
+
const rootClass = className === "rr-share" ? className : `rr-share ${className}`;
|
|
244
|
+
return /* @__PURE__ */ jsxs("nav", { class: rootClass, "data-rr-share": true, "aria-label": ariaLabel, children: [
|
|
245
|
+
/* @__PURE__ */ jsxs("ul", { class: "rr-share__list", children: [
|
|
246
|
+
links.map((link) => /* @__PURE__ */ jsx("li", { class: "rr-share__item", children: /* @__PURE__ */ jsx(
|
|
247
|
+
"a",
|
|
248
|
+
{
|
|
249
|
+
class: `rr-share__link rr-share__link--${link.service}`,
|
|
250
|
+
href: link.url,
|
|
251
|
+
target: "_blank",
|
|
252
|
+
rel: "noopener noreferrer",
|
|
253
|
+
"data-share-service": link.service,
|
|
254
|
+
children: link.label
|
|
255
|
+
}
|
|
256
|
+
) }, link.service)),
|
|
257
|
+
copyUrl ? /* @__PURE__ */ jsx("li", { class: "rr-share__item", children: /* @__PURE__ */ jsx(
|
|
258
|
+
"button",
|
|
259
|
+
{
|
|
260
|
+
type: "button",
|
|
261
|
+
class: "rr-share__button rr-share__copy",
|
|
262
|
+
"data-rr-share-copy": true,
|
|
263
|
+
"data-share-url": copyUrl,
|
|
264
|
+
"data-rr-share-copied": copiedLabel,
|
|
265
|
+
"data-rr-share-copy-failed": copyFailedLabel,
|
|
266
|
+
hidden: true,
|
|
267
|
+
children: copyLabel
|
|
268
|
+
}
|
|
269
|
+
) }) : null
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ jsx("p", { class: "rr-share__status", role: "status", "aria-live": "polite" })
|
|
272
|
+
] });
|
|
273
|
+
});
|
|
274
|
+
var share_controls_default = ShareControls;
|
|
275
|
+
|
|
240
276
|
// src/client.ts
|
|
241
277
|
var COPY_SELECTOR = "[data-rr-share-copy]";
|
|
242
278
|
var STATUS_SELECTOR = ".rr-share__status";
|
|
@@ -302,9 +338,22 @@ function copyWithExecCommand(value) {
|
|
|
302
338
|
}
|
|
303
339
|
|
|
304
340
|
// index.ts
|
|
341
|
+
var getShareLinks = ({ entry, site, options }) => {
|
|
342
|
+
const resolved = resolveShareOptions(options);
|
|
343
|
+
const url = buildAbsoluteUrl(site, entry.permalink);
|
|
344
|
+
return {
|
|
345
|
+
links: buildShareLinks(resolved, { url, title: entry.title }),
|
|
346
|
+
...resolved.services.includes("copy") ? { copyUrl: url } : {},
|
|
347
|
+
ariaLabel: resolved.ariaLabel,
|
|
348
|
+
className: resolved.className || SHARE_ROOT_CLASS,
|
|
349
|
+
copyLabel: resolved.labels.copy,
|
|
350
|
+
copiedLabel: resolved.copiedLabel,
|
|
351
|
+
copyFailedLabel: resolved.copyFailedLabel
|
|
352
|
+
};
|
|
353
|
+
};
|
|
305
354
|
var SHARE_PLUGIN_NAME = "share";
|
|
306
355
|
function share(options = {}) {
|
|
307
|
-
const
|
|
356
|
+
const _resolved = resolveShareOptions(options);
|
|
308
357
|
return definePlugin({
|
|
309
358
|
name: SHARE_PLUGIN_NAME,
|
|
310
359
|
processedContentCache: {
|
|
@@ -313,47 +362,34 @@ function share(options = {}) {
|
|
|
313
362
|
},
|
|
314
363
|
options,
|
|
315
364
|
validateOptions: validateShareOptions,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (!hasSlotFragment(entry.bodySlots?.[slot], block)) {
|
|
327
|
-
appendContentBodySlot(entry, slot, block);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
assets: [createStyleAsset(SHARE_PLUGIN_NAME)],
|
|
332
|
-
clientEntries: [createClientEntry(SHARE_PLUGIN_NAME, "initShare")]
|
|
365
|
+
assets: [
|
|
366
|
+
createStyleAsset(SHARE_PLUGIN_NAME, "@riebeckite/plugin-share/style.css")
|
|
367
|
+
],
|
|
368
|
+
clientEntries: [
|
|
369
|
+
createClientEntry(
|
|
370
|
+
SHARE_PLUGIN_NAME,
|
|
371
|
+
"@riebeckite/plugin-share/client",
|
|
372
|
+
"initShare"
|
|
373
|
+
)
|
|
374
|
+
]
|
|
333
375
|
});
|
|
334
376
|
}
|
|
335
377
|
var sharePlugin = share;
|
|
336
|
-
function hasSlotFragment(slot, fragment) {
|
|
337
|
-
return slot === fragment || slot?.startsWith(`${fragment}
|
|
338
|
-
`) || slot?.endsWith(`
|
|
339
|
-
${fragment}`) || slot?.includes(`
|
|
340
|
-
${fragment}
|
|
341
|
-
`) || false;
|
|
342
|
-
}
|
|
343
378
|
export {
|
|
344
379
|
DEFAULT_SHARE_ARIA_LABEL,
|
|
345
380
|
DEFAULT_SHARE_COPIED_LABEL,
|
|
346
381
|
DEFAULT_SHARE_COPY_FAILED_LABEL,
|
|
347
382
|
DEFAULT_SHARE_LABELS,
|
|
348
|
-
DEFAULT_SHARE_PLACEMENT,
|
|
349
383
|
DEFAULT_SHARE_SERVICES,
|
|
350
384
|
SHARE_ATTRIBUTE,
|
|
351
385
|
SHARE_PLUGIN_NAME,
|
|
352
386
|
SHARE_ROOT_CLASS,
|
|
353
387
|
SHARE_SERVICES,
|
|
388
|
+
share_controls_default as ShareControls,
|
|
354
389
|
buildAbsoluteUrl,
|
|
355
390
|
buildShareLinks,
|
|
356
391
|
buildShareUrl,
|
|
392
|
+
getShareLinks,
|
|
357
393
|
initShare,
|
|
358
394
|
normalizeMastodonInstance,
|
|
359
395
|
renderShareControls,
|
package/dist/src/options.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { ConfigValidationIssue } from "@riebeckite/core";
|
|
2
|
-
import { type ResolvedShareOptions, type ShareOptions, type
|
|
2
|
+
import { type ResolvedShareOptions, type ShareOptions, type ShareService } from "./types.js";
|
|
3
3
|
/** Services rendered when `services` is omitted. Mastodon is opt-in. */
|
|
4
4
|
export declare const DEFAULT_SHARE_SERVICES: readonly ShareService[];
|
|
5
5
|
export declare const DEFAULT_SHARE_LABELS: Readonly<Record<ShareService, string>>;
|
|
6
|
-
export declare const DEFAULT_SHARE_PLACEMENT: SharePlacement;
|
|
7
6
|
export declare const DEFAULT_SHARE_ARIA_LABEL = "Share";
|
|
8
7
|
export declare const DEFAULT_SHARE_COPIED_LABEL = "Copied";
|
|
9
8
|
export declare const DEFAULT_SHARE_COPY_FAILED_LABEL = "Copy failed";
|
package/dist/src/services.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type ShareTarget = {
|
|
|
11
11
|
* Resolves a note's `permalink` to an absolute URL against the site base URL.
|
|
12
12
|
* Already-absolute URLs are returned unchanged.
|
|
13
13
|
*/
|
|
14
|
-
export declare function buildAbsoluteUrl(config: ResolvedRiebeckiteConfig, pathOrUrl: string): string;
|
|
14
|
+
export declare function buildAbsoluteUrl(config: Pick<ResolvedRiebeckiteConfig, "site">, pathOrUrl: string): string;
|
|
15
15
|
/**
|
|
16
16
|
* Normalizes a Mastodon instance to a bare host. Accepts `"mastodon.social"`,
|
|
17
17
|
* `"https://mastodon.social/"`, and `"https://mastodon.social/@user"` (the
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/** Services a note can be shared to. `copy` is the copy-link action. */
|
|
2
2
|
export declare const SHARE_SERVICES: readonly ["x", "bluesky", "mastodon", "facebook", "linkedin", "hatena", "copy"];
|
|
3
3
|
export type ShareService = (typeof SHARE_SERVICES)[number];
|
|
4
|
-
/** Where the controls sit relative to the note body. */
|
|
5
|
-
export type SharePlacement = "top" | "bottom";
|
|
6
4
|
/** Options accepted by `share()`. */
|
|
7
5
|
export type ShareOptions = {
|
|
8
6
|
/**
|
|
@@ -11,8 +9,6 @@ export type ShareOptions = {
|
|
|
11
9
|
* to enable it.
|
|
12
10
|
*/
|
|
13
11
|
services?: readonly ShareService[];
|
|
14
|
-
/** Where the controls appear. Defaults to `"bottom"`. */
|
|
15
|
-
placement?: SharePlacement;
|
|
16
12
|
/**
|
|
17
13
|
* Instance host used by the `mastodon` service, for example
|
|
18
14
|
* `"mastodon.social"`. Accepts a bare host or a full URL; the scheme and
|
|
@@ -37,7 +33,6 @@ export type ShareOptions = {
|
|
|
37
33
|
/** `ShareOptions` with every default applied. */
|
|
38
34
|
export type ResolvedShareOptions = {
|
|
39
35
|
services: readonly ShareService[];
|
|
40
|
-
placement: SharePlacement;
|
|
41
36
|
mastodonInstance: string;
|
|
42
37
|
className: string;
|
|
43
38
|
ariaLabel: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-share",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,10 +18,17 @@
|
|
|
18
18
|
"source": "./client.ts",
|
|
19
19
|
"default": "./dist/client.js"
|
|
20
20
|
},
|
|
21
|
+
"./components": {
|
|
22
|
+
"types": "./dist/components/share-controls.d.ts",
|
|
23
|
+
"import": "./dist/components/share-controls.js",
|
|
24
|
+
"source": "./components/share-controls.tsx",
|
|
25
|
+
"default": "./dist/components/share-controls.js"
|
|
26
|
+
},
|
|
21
27
|
"./style.css": "./style.css"
|
|
22
28
|
},
|
|
23
29
|
"dependencies": {
|
|
24
|
-
"
|
|
30
|
+
"hono": "^4.12.25",
|
|
31
|
+
"@riebeckite/core": "0.0.23"
|
|
25
32
|
},
|
|
26
33
|
"devDependencies": {
|
|
27
34
|
"tsx": "^4.22.4"
|