@riebeckite/plugin-share 0.0.24 → 0.0.25
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/dist/components/share-controls.d.ts +2 -2
- package/dist/components/share-controls.js +181 -10
- package/dist/index.js +21 -10
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ShareLink } from "../src/types.js";
|
|
2
2
|
export type ShareControlsProps = {
|
|
3
|
-
readonly links
|
|
3
|
+
readonly links?: readonly ShareLink[];
|
|
4
4
|
readonly copyUrl?: string;
|
|
5
5
|
readonly ariaLabel?: string;
|
|
6
6
|
readonly className?: string;
|
|
@@ -8,5 +8,5 @@ export type ShareControlsProps = {
|
|
|
8
8
|
readonly copiedLabel?: string;
|
|
9
9
|
readonly copyFailedLabel?: string;
|
|
10
10
|
};
|
|
11
|
-
declare const ShareControls: (
|
|
11
|
+
declare const ShareControls: (props: Readonly<ShareControlsProps>) => Promise<import("hono/utils/html").HtmlEscapedString>;
|
|
12
12
|
export default ShareControls;
|
|
@@ -1,15 +1,183 @@
|
|
|
1
|
+
// components/share-controls.tsx
|
|
2
|
+
import { useArticleContext, useSiteContext } from "@riebeckite/ui";
|
|
3
|
+
import { html } from "hono/html";
|
|
4
|
+
|
|
5
|
+
// index.ts
|
|
6
|
+
import {
|
|
7
|
+
createClientEntry,
|
|
8
|
+
createStyleAsset,
|
|
9
|
+
definePlugin
|
|
10
|
+
} from "@riebeckite/core";
|
|
11
|
+
|
|
12
|
+
// src/services.ts
|
|
13
|
+
function buildAbsoluteUrl(config, pathOrUrl) {
|
|
14
|
+
if (!pathOrUrl) return config.site.baseUrl;
|
|
15
|
+
if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
|
|
16
|
+
return new URL(
|
|
17
|
+
pathOrUrl,
|
|
18
|
+
config.site.baseUrl || "https://example.com"
|
|
19
|
+
).toString();
|
|
20
|
+
}
|
|
21
|
+
function normalizeMastodonInstance(value) {
|
|
22
|
+
if (typeof value !== "string") return "";
|
|
23
|
+
const trimmed = value.trim();
|
|
24
|
+
if (trimmed === "") return "";
|
|
25
|
+
const withoutScheme = trimmed.replace(/^https?:\/\//i, "");
|
|
26
|
+
const host = withoutScheme.split(/[/?#]/, 1)[0] ?? "";
|
|
27
|
+
return host.replace(/\.+$/, "");
|
|
28
|
+
}
|
|
29
|
+
function buildShareUrl(service, target, mastodonInstance = "") {
|
|
30
|
+
const url = target.url;
|
|
31
|
+
const title = target.title;
|
|
32
|
+
const composed = title ? `${title} ${url}` : url;
|
|
33
|
+
switch (service) {
|
|
34
|
+
case "x":
|
|
35
|
+
return `https://twitter.com/intent/tweet?url=${encode(url)}&text=${encode(title)}`;
|
|
36
|
+
case "bluesky":
|
|
37
|
+
return `https://bsky.app/intent/compose?text=${encode(composed)}`;
|
|
38
|
+
case "mastodon": {
|
|
39
|
+
const instance = normalizeMastodonInstance(mastodonInstance);
|
|
40
|
+
if (instance === "") return null;
|
|
41
|
+
return `https://${instance}/share?text=${encode(composed)}`;
|
|
42
|
+
}
|
|
43
|
+
case "facebook":
|
|
44
|
+
return `https://www.facebook.com/sharer/sharer.php?u=${encode(url)}`;
|
|
45
|
+
case "linkedin":
|
|
46
|
+
return `https://www.linkedin.com/sharing/share-offsite/?url=${encode(url)}`;
|
|
47
|
+
case "hatena":
|
|
48
|
+
return `https://b.hatena.ne.jp/add?mode=confirm&url=${encode(url)}&title=${encode(title)}`;
|
|
49
|
+
case "copy":
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function buildShareLinks(options, target) {
|
|
54
|
+
const links = [];
|
|
55
|
+
for (const service of options.services) {
|
|
56
|
+
const url = buildShareUrl(service, target, options.mastodonInstance);
|
|
57
|
+
if (url === null) continue;
|
|
58
|
+
links.push({ service, label: options.labels[service], url });
|
|
59
|
+
}
|
|
60
|
+
return links;
|
|
61
|
+
}
|
|
62
|
+
function encode(value) {
|
|
63
|
+
return encodeURIComponent(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/types.ts
|
|
67
|
+
var SHARE_SERVICES = [
|
|
68
|
+
"x",
|
|
69
|
+
"bluesky",
|
|
70
|
+
"mastodon",
|
|
71
|
+
"facebook",
|
|
72
|
+
"linkedin",
|
|
73
|
+
"hatena",
|
|
74
|
+
"copy"
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// src/options.ts
|
|
78
|
+
var DEFAULT_SHARE_SERVICES = [
|
|
79
|
+
"x",
|
|
80
|
+
"bluesky",
|
|
81
|
+
"facebook",
|
|
82
|
+
"linkedin",
|
|
83
|
+
"hatena",
|
|
84
|
+
"copy"
|
|
85
|
+
];
|
|
86
|
+
var DEFAULT_SHARE_LABELS = {
|
|
87
|
+
x: "X",
|
|
88
|
+
bluesky: "Bluesky",
|
|
89
|
+
mastodon: "Mastodon",
|
|
90
|
+
facebook: "Facebook",
|
|
91
|
+
linkedin: "LinkedIn",
|
|
92
|
+
hatena: "Hatena Bookmark",
|
|
93
|
+
copy: "Copy link"
|
|
94
|
+
};
|
|
95
|
+
var DEFAULT_SHARE_ARIA_LABEL = "Share";
|
|
96
|
+
var DEFAULT_SHARE_COPIED_LABEL = "Copied";
|
|
97
|
+
var DEFAULT_SHARE_COPY_FAILED_LABEL = "Copy failed";
|
|
98
|
+
function resolveShareOptions(options = {}) {
|
|
99
|
+
return {
|
|
100
|
+
services: normalizeServices(options.services),
|
|
101
|
+
mastodonInstance: normalizeMastodonInstance(options.mastodonInstance),
|
|
102
|
+
className: normalizeNonEmpty(options.className, ""),
|
|
103
|
+
ariaLabel: normalizeNonEmpty(options.ariaLabel, DEFAULT_SHARE_ARIA_LABEL),
|
|
104
|
+
labels: { ...DEFAULT_SHARE_LABELS, ...sanitizeLabels(options.labels) },
|
|
105
|
+
copiedLabel: normalizeNonEmpty(
|
|
106
|
+
options.copiedLabel,
|
|
107
|
+
DEFAULT_SHARE_COPIED_LABEL
|
|
108
|
+
),
|
|
109
|
+
copyFailedLabel: normalizeNonEmpty(
|
|
110
|
+
options.copyFailedLabel,
|
|
111
|
+
DEFAULT_SHARE_COPY_FAILED_LABEL
|
|
112
|
+
)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function normalizeServices(services) {
|
|
116
|
+
const source = services ?? DEFAULT_SHARE_SERVICES;
|
|
117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
118
|
+
const resolved = [];
|
|
119
|
+
for (const service of source) {
|
|
120
|
+
if (!SHARE_SERVICES.includes(service)) continue;
|
|
121
|
+
if (seen.has(service)) continue;
|
|
122
|
+
seen.add(service);
|
|
123
|
+
resolved.push(service);
|
|
124
|
+
}
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
function sanitizeLabels(labels) {
|
|
128
|
+
if (!labels || typeof labels !== "object") return {};
|
|
129
|
+
const resolved = {};
|
|
130
|
+
for (const service of SHARE_SERVICES) {
|
|
131
|
+
const value = labels[service];
|
|
132
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
133
|
+
resolved[service] = value.trim();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return resolved;
|
|
137
|
+
}
|
|
138
|
+
function normalizeNonEmpty(value, fallback) {
|
|
139
|
+
if (typeof value !== "string") return fallback;
|
|
140
|
+
const trimmed = value.trim();
|
|
141
|
+
return trimmed === "" ? fallback : trimmed;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/render.ts
|
|
145
|
+
import { escapeHtml, escapeHtmlAttribute } from "@riebeckite/core";
|
|
146
|
+
var SHARE_ROOT_CLASS = "rr-share";
|
|
147
|
+
|
|
148
|
+
// index.ts
|
|
149
|
+
var getShareLinks = ({ entry, site, options }) => {
|
|
150
|
+
const resolved = resolveShareOptions(options);
|
|
151
|
+
const url = buildAbsoluteUrl(site, entry.permalink);
|
|
152
|
+
return {
|
|
153
|
+
links: buildShareLinks(resolved, { url, title: entry.title }),
|
|
154
|
+
...resolved.services.includes("copy") ? { copyUrl: url } : {},
|
|
155
|
+
ariaLabel: resolved.ariaLabel,
|
|
156
|
+
className: resolved.className || SHARE_ROOT_CLASS,
|
|
157
|
+
copyLabel: resolved.labels.copy,
|
|
158
|
+
copiedLabel: resolved.copiedLabel,
|
|
159
|
+
copyFailedLabel: resolved.copyFailedLabel
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
1
163
|
// components/share-controls.tsx
|
|
2
164
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
3
|
-
var ShareControls = (({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
165
|
+
var ShareControls = (async (props) => {
|
|
166
|
+
const site = useSiteContext();
|
|
167
|
+
const article = useArticleContext();
|
|
168
|
+
const resolved = props.links === void 0 && site && article?.entry ? await getShareLinks({
|
|
169
|
+
entry: article.entry,
|
|
170
|
+
site: site.config,
|
|
171
|
+
options: findShareOptions(site.config.plugins)
|
|
172
|
+
}) : void 0;
|
|
173
|
+
const links = props.links ?? resolved?.links ?? [];
|
|
174
|
+
const copyUrl = props.copyUrl ?? resolved?.copyUrl;
|
|
175
|
+
const ariaLabel = props.ariaLabel ?? resolved?.ariaLabel ?? "Share";
|
|
176
|
+
const className = props.className ?? resolved?.className ?? "rr-share";
|
|
177
|
+
const copyLabel = props.copyLabel ?? resolved?.copyLabel ?? "Copy link";
|
|
178
|
+
const copiedLabel = props.copiedLabel ?? resolved?.copiedLabel ?? "Copied";
|
|
179
|
+
const copyFailedLabel = props.copyFailedLabel ?? resolved?.copyFailedLabel ?? "Copy failed";
|
|
180
|
+
if (links.length === 0 && !copyUrl) return html``;
|
|
13
181
|
const rootClass = className === "rr-share" ? className : `rr-share ${className}`;
|
|
14
182
|
return /* @__PURE__ */ jsxs("nav", { class: rootClass, "data-rr-share": true, "aria-label": ariaLabel, children: [
|
|
15
183
|
/* @__PURE__ */ jsxs("ul", { class: "rr-share__list", children: [
|
|
@@ -42,6 +210,9 @@ var ShareControls = (({
|
|
|
42
210
|
] });
|
|
43
211
|
});
|
|
44
212
|
var share_controls_default = ShareControls;
|
|
213
|
+
function findShareOptions(plugins) {
|
|
214
|
+
return plugins.find((plugin) => plugin.name === "share")?.options;
|
|
215
|
+
}
|
|
45
216
|
export {
|
|
46
217
|
share_controls_default as default
|
|
47
218
|
};
|
package/dist/index.js
CHANGED
|
@@ -229,17 +229,25 @@ function renderCopyItem(options, target) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// components/share-controls.tsx
|
|
232
|
+
import { useArticleContext, useSiteContext } from "@riebeckite/ui";
|
|
233
|
+
import { html } from "hono/html";
|
|
232
234
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
233
|
-
var ShareControls = (({
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
235
|
+
var ShareControls = (async (props) => {
|
|
236
|
+
const site = useSiteContext();
|
|
237
|
+
const article = useArticleContext();
|
|
238
|
+
const resolved = props.links === void 0 && site && article?.entry ? await getShareLinks({
|
|
239
|
+
entry: article.entry,
|
|
240
|
+
site: site.config,
|
|
241
|
+
options: findShareOptions(site.config.plugins)
|
|
242
|
+
}) : void 0;
|
|
243
|
+
const links = props.links ?? resolved?.links ?? [];
|
|
244
|
+
const copyUrl = props.copyUrl ?? resolved?.copyUrl;
|
|
245
|
+
const ariaLabel = props.ariaLabel ?? resolved?.ariaLabel ?? "Share";
|
|
246
|
+
const className = props.className ?? resolved?.className ?? "rr-share";
|
|
247
|
+
const copyLabel = props.copyLabel ?? resolved?.copyLabel ?? "Copy link";
|
|
248
|
+
const copiedLabel = props.copiedLabel ?? resolved?.copiedLabel ?? "Copied";
|
|
249
|
+
const copyFailedLabel = props.copyFailedLabel ?? resolved?.copyFailedLabel ?? "Copy failed";
|
|
250
|
+
if (links.length === 0 && !copyUrl) return html``;
|
|
243
251
|
const rootClass = className === "rr-share" ? className : `rr-share ${className}`;
|
|
244
252
|
return /* @__PURE__ */ jsxs("nav", { class: rootClass, "data-rr-share": true, "aria-label": ariaLabel, children: [
|
|
245
253
|
/* @__PURE__ */ jsxs("ul", { class: "rr-share__list", children: [
|
|
@@ -272,6 +280,9 @@ var ShareControls = (({
|
|
|
272
280
|
] });
|
|
273
281
|
});
|
|
274
282
|
var share_controls_default = ShareControls;
|
|
283
|
+
function findShareOptions(plugins) {
|
|
284
|
+
return plugins.find((plugin) => plugin.name === "share")?.options;
|
|
285
|
+
}
|
|
275
286
|
|
|
276
287
|
// src/client.ts
|
|
277
288
|
var COPY_SELECTOR = "[data-rr-share-copy]";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-share",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"hono": "^4.12.25",
|
|
31
|
-
"@riebeckite/core": "0.0.
|
|
31
|
+
"@riebeckite/core": "0.0.25",
|
|
32
|
+
"@riebeckite/ui": "0.0.25"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"tsx": "^4.22.4"
|