aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-07 12:44:17 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-11-07 12:44:17 +0900
commit4694675635a449f6537272ddad2feb2a4724c41d (patch)
tree8d376f782eae7cff6d09e35afe0b12349112614d /src
parent5c56c4f74225ef9f8a5e992228cdab17eb029bea (diff)
events: 日付の改行と行ごとの共有ボタンを追加
「令和9年1月28日(木)」のような日付で列が広がるため、「令和〇年」の 直後で改行するようにした。各行にX・Instagram・Facebookの共有ボタンを 付け、日付・タイトル・時間と場所・申込みを文面にして共有する。 ボタンは行数が多いためHTMLには書かず、スクリプトで差し込んでいる。
Diffstat (limited to 'src')
-rw-r--r--src/components/ScheduleTable.astro118
-rw-r--r--src/styles/custom.css45
2 files changed, 162 insertions, 1 deletions
diff --git a/src/components/ScheduleTable.astro b/src/components/ScheduleTable.astro
index dcf210d..870cd77 100644
--- a/src/components/ScheduleTable.astro
+++ b/src/components/ScheduleTable.astro
@@ -27,6 +27,10 @@ if (scope === 'main') {
}
const data: TableData = { columns: base.columns, rows: list };
+
+// 「令和9年1月28日(木)」のような長い日付で列が広がるため、「令和9年」の直後で改行する。
+const dateHtml = (html: string) =>
+ html.replace(/(令和[0-90-9]+年)(?!\s*<br)/, '$1<br />');
---
<table>
@@ -37,7 +41,7 @@ const data: TableData = { columns: base.columns, rows: list };
{
data.rows.map((row) => (
<tr>
- <td set:html={row.date} />
+ <td set:html={dateHtml(row.date)} />
<td set:html={row.title} />
<td set:html={row.place} />
<td set:html={row.note} />
@@ -47,3 +51,115 @@ const data: TableData = { columns: base.columns, rows: list };
}
</tbody>
</table>
+
+<script>
+ // 行ごとの共有ボタン(X・Instagram・Facebook)。
+ // 行数が多いため、HTMLには書かず、このスクリプトで後から差し込む。
+ // アイコンは CSS(mask-image)で描くので、ボタンには文字もSVGも入れない。
+ if (!document.documentElement.dataset.scheduleShareInit) {
+ document.documentElement.dataset.scheduleShareInit = '1';
+
+ const KINDS: { kind: string; label: string }[] = [
+ { kind: 'x', label: 'Xでシェア' },
+ { kind: 'instagram', label: 'Instagramでシェア' },
+ { kind: 'facebook', label: 'Facebookでシェア' },
+ ];
+
+ function attachButtons() {
+ for (const row of document.querySelectorAll<HTMLTableRowElement>(
+ '.schedule-table tbody tr',
+ )) {
+ const titleCell = row.children[1];
+ if (!titleCell || titleCell.querySelector('.schedule-share')) continue;
+
+ const wrap = document.createElement('span');
+ wrap.className = 'schedule-share';
+ for (const { kind, label } of KINDS) {
+ const button = document.createElement('button');
+ button.type = 'button';
+ button.className = `schedule-share-btn is-${kind}`;
+ button.dataset.share = kind;
+ button.setAttribute('aria-label', label);
+ button.title = label;
+ wrap.appendChild(button);
+ }
+ titleCell.appendChild(wrap);
+ }
+ }
+
+ const strip = (el: Element | null | undefined) =>
+ (el?.textContent ?? '').replace(/\s+/g, ' ').trim();
+
+ // 共有する文面(日時+タイトル+時間と場所+申込み等)
+ function shareText(row: Element): string {
+ const cells = row.children;
+ const lines = [`${strip(cells[0])} ${strip(cells[1])}`];
+ const place = strip(cells[2]);
+ const note = strip(cells[3]);
+ if (place) lines.push(place);
+ if (note) lines.push(note.length > 120 ? `${note.slice(0, 120)}…` : note);
+ return lines.join('\n');
+ }
+
+ function rowUrl(row: Element): string {
+ const base = location.origin + location.pathname;
+ return row.id ? `${base}#${row.id}` : base;
+ }
+
+ document.addEventListener('click', async (event) => {
+ const target = event.target as HTMLElement | null;
+ const button = target?.closest<HTMLElement>('.schedule-share-btn');
+ if (!button) return;
+ const row = button.closest('tr');
+ if (!row) return;
+
+ const text = shareText(row);
+ const url = rowUrl(row);
+ const kind = button.dataset.share;
+
+ if (kind === 'x') {
+ window.open(
+ `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(url)}`,
+ '_blank',
+ 'noopener',
+ );
+ return;
+ }
+
+ if (kind === 'facebook') {
+ // Facebookのsharerは文面を指定できない(ページのOGPが使われる)
+ window.open(
+ `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,
+ '_blank',
+ 'noopener',
+ );
+ return;
+ }
+
+ // Instagramはテキストを渡せる共有URLがないため、端末の共有メニューを使う
+ const share = (navigator as Navigator & { share?: (data: ShareData) => Promise<void> })
+ .share;
+ if (share) {
+ try {
+ await share.call(navigator, { text, url });
+ } catch {
+ // キャンセルされた場合は何もしない
+ }
+ return;
+ }
+
+ try {
+ await navigator.clipboard.writeText(`${text}\n${url}`);
+ window.alert(
+ 'Instagram はスマートフォンの共有メニューから投稿できます(このブラウザは共有メニューに対応していません)。共有する文面をコピーしました。',
+ );
+ } catch {
+ window.alert('Instagram はスマートフォンの共有メニューから投稿できます。');
+ }
+ });
+
+ attachButtons();
+ // アーカイブの絞り込みなどで行が増えた場合にも備える
+ document.addEventListener('click', () => setTimeout(attachButtons, 0));
+ }
+</script>
diff --git a/src/styles/custom.css b/src/styles/custom.css
index 843599c..4fd4829 100644
--- a/src/styles/custom.css
+++ b/src/styles/custom.css
@@ -297,6 +297,51 @@ body:has(.parks-controls) .sl-markdown-content p {
white-space: nowrap;
width: 1%;
}
+/* 行ごとの共有ボタン(アイコンは mask-image で描く) */
+.schedule-share {
+ display: inline-flex;
+ gap: 0.25rem;
+ margin-left: 0.5rem;
+ vertical-align: -2px;
+ white-space: nowrap;
+}
+.schedule-share-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.35rem;
+ height: 1.35rem;
+ padding: 0;
+ border: 1px solid var(--sl-color-gray-5);
+ border-radius: 4px;
+ background: var(--sl-color-bg);
+ color: var(--sl-color-gray-3);
+ cursor: pointer;
+ line-height: 1;
+ opacity: 0.8;
+}
+.schedule-share-btn::before {
+ content: "";
+ width: 14px;
+ height: 14px;
+ background: currentColor;
+ -webkit-mask: var(--share-icon) center / contain no-repeat;
+ mask: var(--share-icon) center / contain no-repeat;
+}
+.schedule-share-btn:hover {
+ color: var(--sl-color-accent);
+ border-color: var(--sl-color-accent);
+ opacity: 1;
+}
+.schedule-share-btn.is-x {
+ --share-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27M18.244%202.25h3.308l-7.227%208.26%208.502%2011.24H16.17l-5.214-6.817L4.99%2021.75H1.68l7.73-8.835L1.254%202.25H8.08l4.713%206.231%205.451-6.231Zm-1.161%2017.52h1.833L7.084%204.126H5.117l11.966%2015.644Z%27%2F%3E%3C%2Fsvg%3E");
+}
+.schedule-share-btn.is-instagram {
+ --share-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%3E%3Crect%20x%3D%273%27%20y%3D%273%27%20width%3D%2718%27%20height%3D%2718%27%20rx%3D%275%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272%27%2F%3E%3Ccircle%20cx%3D%2712%27%20cy%3D%2712%27%20r%3D%274%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272%27%2F%3E%3Ccircle%20cx%3D%2717.2%27%20cy%3D%276.8%27%20r%3D%271.2%27%2F%3E%3C%2Fsvg%3E");
+}
+.schedule-share-btn.is-facebook {
+ --share-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27M22%2012a10%2010%200%201%200-11.6%209.9v-7H7.9V12h2.5V9.8c0-2.5%201.5-3.9%203.8-3.9%201.1%200%202.2.2%202.2.2v2.4h-1.2c-1.2%200-1.6.8-1.6%201.6V12h2.7l-.4%202.9h-2.3v7A10%2010%200%200%200%2022%2012Z%27%2F%3E%3C%2Fsvg%3E");
+}
/* ── 冒頭の英語・中国語・韓国語・ベトナム語の案内 ── */
.lang-intro {