aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/components
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/components
parent5c56c4f74225ef9f8a5e992228cdab17eb029bea (diff)
events: 日付の改行と行ごとの共有ボタンを追加
「令和9年1月28日(木)」のような日付で列が広がるため、「令和〇年」の 直後で改行するようにした。各行にX・Instagram・Facebookの共有ボタンを 付け、日付・タイトル・時間と場所・申込みを文面にして共有する。 ボタンは行数が多いためHTMLには書かず、スクリプトで差し込んでいる。
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ScheduleTable.astro118
1 files changed, 117 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>