PageSourceSearch

https://app.saii.kr/assets/lib/message_menu_markup-77480109.js

js saii.kr collected 2026-10-06 03:46:52 UTC 4,292 bytes, 117 lines download raw bytes

1import { reactionLabels } from "lib/message_reaction_markup"
2
3// The long-press menu is rendered twice: in messages/_message_actions_menu
4// .html.erb for the server pass, and here for messages that arrive over the
5// socket. Labels come from the same hidden element the reaction row reads, so
6// a socket message stays in the viewer's language.
7//
8// The socket-built <li> carries only the message-reactions and message-actions
9// controllers, so this menu offers react, copy and save. Correct needs markup
10// that only the server pass renders; it appears on the next load.
11const FALLBACK_LABELS = {
12  menu: "Message actions",
13  reply: "Reply",
14  copy: "Copy",
15  copied: "Copied"
16}
17
18export function menuLabels() {
19  const element = document.getElementById("message-reaction-labels")
20  if (!element) return { ...FALLBACK_LABELS }
21
22  return {
23    menu: element.dataset.menuLabel || FALLBACK_LABELS.menu,
24    reply: element.dataset.replyLabel || FALLBACK_LABELS.reply,
25    copy: element.dataset.copyLabel || FALLBACK_LABELS.copy,
26    copied: element.dataset.copiedLabel || FALLBACK_LABELS.copied
27  }
28}
29
30export function buildMessageMenu(messageId, { saveForm = null, reply = true } = {}) {
31  const labels = { ...reactionLabels(), ...menuLabels() }
32
33  const overlay = document.createElement("div")
34  overlay.className = "kb-message-menu"
35  overlay.dataset.messageActionsTarget = "menu"
36  overlay.hidden = true
37
38  const card = document.createElement("div")
39  card.className = "kb-message-menu__card kb-message-menu__card--closed"
40  card.dataset.messageActionsTarget = "card"
41  card.dataset.messageMenu = messageId
42  card.setAttribute("role", "menu")
43  card.setAttribute("aria-label", labels.menu)
44
45  const reactions = document.createElement("div")
46  reactions.className = "kb-message-menu__reactions"
47  reactions.setAttribute("role", "group")
48  reactions.setAttribute("aria-label", labels.picker)
49
50  labels.emojis.forEach((emoji) => {
51    const option = document.createElement("button")
52    option.type = "button"
53    option.className = "kb-message-menu__emoji"
54    option.dataset.action = "message-reactions#select message-actions#close"
55    option.dataset.messageReactionEmoji = emoji
56    option.setAttribute("role", "menuitem")
57    option.setAttribute("aria-pressed", "false")
58    option.setAttribute("aria-label", labels.reactWith.replace("%{emoji}", emoji))
59
60    const glyph = document.createElement("span")
61    glyph.setAttribute("aria-hidden", "true")
62    glyph.textContent = emoji
63    option.appendChild(glyph)
64    reactions.appendChild(option)
65  })
66
67  const items = document.createElement("div")
68  items.className = "kb-message-menu__items"
69
70  if (reply) {
71    const replyButton = document.createElement("button")
72    replyButton.type = "button"
73    replyButton.className = "kb-message-menu__item"
74    replyButton.dataset.action = "message-actions#reply"
75    replyButton.dataset.messageMenuAction = "reply"
76    replyButton.setAttribute("role", "menuitem")
77    replyButton.textContent = labels.reply
78    items.appendChild(replyButton)
79  }
80
81  const copy = document.createElement("button")
82  copy.type = "button"
83  copy.className = "kb-message-menu__item"
84  copy.dataset.action = "message-actions#copy"
85  copy.dataset.messageMenuAction = "copy"
86  copy.setAttribute("role", "menuitem")
87  copy.textContent = labels.copy
88  items.appendChild(copy)
89
90  if (saveForm) items.appendChild(saveForm)
91
92  card.append(reactions, items)
93  overlay.appendChild(card)
94  return overlay
95}
96
97// Keyboard and screen-reader path into the menu; hidden until focused.
98export function buildMenuTrigger() {
99  const trigger = document.createElement("button")
100  trigger.type = "button"
101  trigger.className = "kb-message-menu-trigger text-[11px] font-semibold"
102  trigger.style.color = "var(--kb-caption)"
103  trigger.dataset.messageActionsTarget = "trigger"
104  trigger.dataset.action = "message-actions#open"
105  trigger.setAttribute("aria-haspopup", "menu")
106  trigger.setAttribute("aria-expanded", "false")
107  trigger.textContent = menuLabels().menu
108  return trigger
109}
110
111export const BUBBLE_PRESS_ACTIONS = [
112  "touchstart->message-actions#pressStart",
113  "touchend->message-actions#pressEnd",
114  "touchmove->message-actions#pressMove",
115  "touchcancel->message-actions#pressEnd",
116  "contextmenu->message-actions#open"
117].join(" ")

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.