@brightspot/ui 6.4.2 → 6.4.3-pr.366.2026.10.5
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/custom-elements.json +499 -499
- package/dist/storybook/assets/{ActionBar.stories-S00Ek6J4.js → ActionBar.stories-0ScNWOC4.js} +1 -1
- package/dist/storybook/assets/{ActionItem.stories-D4F9yCyV.js → ActionItem.stories-DXJ_yKW3.js} +1 -1
- package/dist/storybook/assets/{AiSparkle.stories-vC8jum9o.js → AiSparkle.stories-Z_5khRM7.js} +1 -1
- package/dist/storybook/assets/{Avatar.stories-BvCzPIfr.js → Avatar.stories-C9XcQNHg.js} +1 -1
- package/dist/storybook/assets/{AvatarGroup.stories-VRTsctUo.js → AvatarGroup.stories-Cdy_mkeq.js} +1 -1
- package/dist/storybook/assets/{Badge.stories-DPaTwNDd.js → Badge.stories-BUdXSprE.js} +1 -1
- package/dist/storybook/assets/{Banner.stories-Ki6o6Qnz.js → Banner.stories-D71fgNb5.js} +1 -1
- package/dist/storybook/assets/{Button-BVkUpjw-.js → Button-D7pK-2at.js} +1 -1
- package/dist/storybook/assets/{Button.stories-B4Cajufa.js → Button.stories-B6WKuxXx.js} +1 -1
- package/dist/storybook/assets/{ButtonGroup.stories--XdEhkoR.js → ButtonGroup.stories-CwiRfEal.js} +1 -1
- package/dist/storybook/assets/{Card.stories-DdaTXloZ.js → Card.stories-CxCcO9k6.js} +1 -1
- package/dist/storybook/assets/{Celebrate.stories-5krJLBnV.js → Celebrate.stories-DNVFUrhg.js} +1 -1
- package/dist/storybook/assets/{Checkbox.stories-DJiir-4S.js → Checkbox.stories-SuP4ltMI.js} +1 -1
- package/dist/storybook/assets/{CircularProgress.stories-FoLbJ02D.js → CircularProgress.stories-BB0twcGv.js} +1 -1
- package/dist/storybook/assets/{ClipboardMixin.stories-C_Y93Vvc.js → ClipboardMixin.stories-DaPV19jG.js} +1 -1
- package/dist/storybook/assets/{Color-6BZIO3FS-Cal-f26G.js → Color-6BZIO3FS-DQ34tmQu.js} +1 -1
- package/dist/storybook/assets/{Colors.stories-BIOYpAZL.js → Colors.stories-CMFJai_a.js} +1 -1
- package/dist/storybook/assets/{CombinedEffects.stories-z1_xU5i7.js → CombinedEffects.stories-BIHfDsN0.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin-DCnHVb7T.js → ComponentStatesMixin-Bq-OGuus.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin.stories-Crysvxtr.js → ComponentStatesMixin.stories-DMxDqidh.js} +1 -1
- package/dist/storybook/assets/{CopyToClipboard.stories-CMpzwyh6.js → CopyToClipboard.stories-CkdYHm8U.js} +1 -1
- package/dist/storybook/assets/{Debounce.stories-C2FgBZXP.js → Debounce.stories-CZSMfS-h.js} +1 -1
- package/dist/storybook/assets/{DocsRenderer-LL677BLK-CxHYlXfz.js → DocsRenderer-LL677BLK-CCAvPHmY.js} +3 -3
- package/dist/storybook/assets/{Dropdown.stories-DhBn1nVs.js → Dropdown.stories-BTqSRQiC.js} +1 -1
- package/dist/storybook/assets/{EmptyState.stories-CBIvJ1Tk.js → EmptyState.stories-CjIkNVv4.js} +1 -1
- package/dist/storybook/assets/{EscaAvatar.stories-DKPhkhvA.js → EscaAvatar.stories-ORFCzxQw.js} +1 -1
- package/dist/storybook/assets/{Events.stories-DLbfdSXr.js → Events.stories-DHFT6dZM.js} +1 -1
- package/dist/storybook/assets/{Heading.stories-B6CIwwiJ.js → Heading.stories-CRgKl3Ve.js} +1 -1
- package/dist/storybook/assets/{HueRipple.stories-C8E72qX_.js → HueRipple.stories-YiQHrWPS.js} +1 -1
- package/dist/storybook/assets/{Icon.stories-YUubIb9i.js → Icon.stories-Bv4QRZNL.js} +1 -1
- package/dist/storybook/assets/{IconButton.stories-07ADapHQ.js → IconButton.stories-ErFc860x.js} +1 -1
- package/dist/storybook/assets/{LinearProgress.stories-DZk0i6LC.js → LinearProgress.stories-DUAAMzdt.js} +1 -1
- package/dist/storybook/assets/{Message.stories-BaTncCPY.js → Message.stories-DxJEEd1I.js} +1 -1
- package/dist/storybook/assets/{OnFindMixin.stories-BKgCI6zb.js → OnFindMixin.stories-CoKUEhb4.js} +1 -1
- package/dist/storybook/assets/{Pagination.stories-BrD-TcnC.js → Pagination.stories-6k02-Zb5.js} +1 -1
- package/dist/storybook/assets/{Popover.stories-CUe375fR.js → Popover.stories-Cd7P1nw_.js} +1 -1
- package/dist/storybook/assets/{ReadyMixin-XQ_tHzRU.js → ReadyMixin-BL65bbm_.js} +1 -1
- package/dist/storybook/assets/{RovingTabindexMixin.stories-BBc7zdjl.js → RovingTabindexMixin.stories-CvtQ9Om6.js} +1 -1
- package/dist/storybook/assets/{Rtc.stories-CkEgMrqd.js → Rtc.stories-BBmO6Ses.js} +1 -1
- package/dist/storybook/assets/{ScrollShadow.stories-DdOKLFJ5.js → ScrollShadow.stories-BUiypzkp.js} +1 -1
- package/dist/storybook/assets/{Switch.stories-Dw9Jic7Y.js → Switch.stories-BndbX0N7.js} +1 -1
- package/dist/storybook/assets/{Tab.stories-CUAvvpwI.js → Tab.stories-DVMyUFjq.js} +1 -1
- package/dist/storybook/assets/{Tabs.stories--iRj2mWu.js → Tabs.stories-BQPYcQ6j.js} +1 -1
- package/dist/storybook/assets/{Throttle.stories-BDuNJMQg.js → Throttle.stories-CKYOCVnj.js} +1 -1
- package/dist/storybook/assets/{Tooltip.stories-RaSZ71_I.js → Tooltip.stories-B3DmBl8X.js} +1 -1
- package/dist/storybook/assets/{Upload.stories-hhhR_V85.js → Upload.stories-nzkKN-_d.js} +1 -1
- package/dist/storybook/assets/{UploadItem.stories-B-1lkPzO.js → UploadItem.stories-CL3oKT8D.js} +1 -1
- package/dist/storybook/assets/{Welcome.stories-BycQWbzd.js → Welcome.stories-DfHnb5cA.js} +1 -1
- package/dist/storybook/assets/{Widget.stories-BDcpbQXU.js → Widget.stories-CcVwc0wv.js} +1 -1
- package/dist/storybook/assets/{WithTooltip-65CFNBJE-CHL0JiUq.js → WithTooltip-65CFNBJE-CxSJYFKf.js} +1 -1
- package/dist/storybook/assets/{blocks-DllEBdtP.js → blocks-A7XjcsrR.js} +5 -5
- package/dist/storybook/assets/{formatter-EIJCOSYU-LrYXshB9.js → formatter-EIJCOSYU-BJ6Z7p9H.js} +1 -1
- package/dist/storybook/assets/if-defined-C9mNa1xD.js +1 -0
- package/dist/storybook/assets/{iframe-Bp7-_nUZ.js → iframe-ChxxW2FZ.js} +4 -4
- package/dist/storybook/assets/iframe-DI_vgyKl.css +1 -0
- package/dist/storybook/assets/{index-Cc3M657Z.js → index-ChSJHRK6.js} +1 -1
- package/dist/storybook/assets/{onFind-Du4z1ObO.js → onFind-C-gvwlyW.js} +1 -1
- package/dist/storybook/assets/{onFind.stories-BCUiC6a9.js → onFind.stories-8tPEGWhS.js} +1 -1
- package/dist/storybook/assets/{onRemove.stories-B-ICGMhl.js → onRemove.stories-CSuoNbAk.js} +1 -1
- package/dist/storybook/assets/{onVisible.stories-DijYge1X.js → onVisible.stories-LFpM1FnE.js} +1 -1
- package/dist/storybook/assets/{style-map-DaohL-ne.js → style-map-4fqj8G4H.js} +1 -1
- package/dist/storybook/assets/{syntaxhighlighter-ED5Y7EFY-CH5_k0Po.js → syntaxhighlighter-ED5Y7EFY-DCcLcHdT.js} +1 -1
- package/dist/storybook/iframe.html +2 -2
- package/dist/storybook/project.json +1 -1
- package/dist/tailwind-plugin-badge.js +2 -1
- package/dist/tailwind-plugin-badge.js.map +1 -1
- package/dist/tailwind-plugin-badge.ts +2 -1
- package/dist/tailwind-plugin-message.js +3 -0
- package/dist/tailwind-plugin-message.js.map +1 -1
- package/dist/tailwind-plugin-message.ts +3 -0
- package/package.json +1 -1
- package/src/legacy/tool-ui/src/Conversation.css +14 -3
- package/src/legacy/tool-ui/src/Conversation.ts +3 -0
- package/src/legacy/tool-ui/src/Entry.ts +4 -3
- package/src/legacy/tool-ui/src/SharePreview.css +209 -4
- package/src/legacy/tool-ui/src/SharePreviewCommenting.ts +659 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/LocationMap.8c328ae48113c22fec31.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/{RTEProseMirror.ef21fd86df209b8f772b.js → RTEProseMirror.e4cbe3b9e2e8f938cf1a.js} +5 -5
- package/src/legacy/tool-ui/src/main/webapp/dist/RegionMap.17d7cd9f92c923b593d5.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/{v4.83bc23585a616647b793.js → v4.56e25b6f00e10789ff37.js} +8 -8
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.22ce1a2b135f2c813878.css +5 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/{v5.dae3cbc9fb1335ca0f1f.js → v5.85bedd855e1e6f2c2caa.js} +6 -6
- package/dist/storybook/assets/if-defined-DONC-k_n.js +0 -1
- package/dist/storybook/assets/iframe-D7jBrFjV.css +0 -1
- package/src/legacy/tool-ui/src/main/webapp/dist/LocationMap.72a510c0f5ff236f596e.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/dist/RegionMap.f6f963eda2cb616b9661.js +0 -1
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.d87bbcc711f912ae68db.css +0 -5
- /package/src/legacy/tool-ui/src/main/webapp/dist/{RTEProseMirror.ef21fd86df209b8f772b.js.LICENSE.txt → RTEProseMirror.e4cbe3b9e2e8f938cf1a.js.LICENSE.txt} +0 -0
- /package/src/legacy/tool-ui/src/main/webapp/dist/{v4.83bc23585a616647b793.js.LICENSE.txt → v4.56e25b6f00e10789ff37.js.LICENSE.txt} +0 -0
- /package/src/legacy/tool-ui/src/main/webapp/dist/{v5.dae3cbc9fb1335ca0f1f.js.LICENSE.txt → v5.85bedd855e1e6f2c2caa.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
import onFind from './dom/onFind'
|
|
2
|
+
import onRTEReady from './dom/onRTEReady'
|
|
3
|
+
import Cookies from 'js-cookie'
|
|
4
|
+
import rtc from './rtc'
|
|
5
|
+
import Tether from './dom/Tether'
|
|
6
|
+
|
|
7
|
+
interface PinData {
|
|
8
|
+
id: string
|
|
9
|
+
xPercent: number
|
|
10
|
+
yPercent: number
|
|
11
|
+
initials: string
|
|
12
|
+
colorIndex: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PostBroadcastData {
|
|
16
|
+
contentId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ScrollState {
|
|
20
|
+
scrollX: number
|
|
21
|
+
scrollY: number
|
|
22
|
+
docWidth: number
|
|
23
|
+
docHeight: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PendingClick {
|
|
27
|
+
clientX: number
|
|
28
|
+
clientY: number
|
|
29
|
+
overlayRect: DOMRect
|
|
30
|
+
previewId: string
|
|
31
|
+
contentId: string
|
|
32
|
+
overlayId: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface PinPopoverOptions {
|
|
36
|
+
clientX: number
|
|
37
|
+
clientY: number
|
|
38
|
+
xPercent: number
|
|
39
|
+
yPercent: number
|
|
40
|
+
contentId: string
|
|
41
|
+
overlayId: string
|
|
42
|
+
previewId: string
|
|
43
|
+
overlay: HTMLElement
|
|
44
|
+
onSuccess: () => void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// If the preview never reports scroll data (e.g. its CSP blocks the injected
|
|
48
|
+
// script), fall back to a same-origin read after this delay so pins still render.
|
|
49
|
+
const SCROLL_DATA_FALLBACK_MS = 1500
|
|
50
|
+
|
|
51
|
+
// ─── Panel toggle ────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
onFind('.SharePreview-toggle', (toggle: HTMLElement) => {
|
|
54
|
+
const sharePreview = toggle.closest<HTMLElement>('.SharePreview')
|
|
55
|
+
if (!sharePreview) return
|
|
56
|
+
|
|
57
|
+
// Lazy-init the pin overlay on the first open. The toggle button lives inside
|
|
58
|
+
// the XHR-loaded frame, so onFind fires only after that frame is fully loaded —
|
|
59
|
+
// meaning the preview iframe and pinsOverlay are already in the DOM.
|
|
60
|
+
let overlayReady = false
|
|
61
|
+
|
|
62
|
+
function maybeInit() {
|
|
63
|
+
if (overlayReady) return
|
|
64
|
+
overlayReady = true
|
|
65
|
+
initCommentingOverlay(sharePreview!)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// If the page loaded with commenting already open (e.g. from a shared preview
|
|
69
|
+
// comment link), init now — the frame is already loaded at this point.
|
|
70
|
+
if (sharePreview.classList.contains('is-commenting-open')) {
|
|
71
|
+
maybeInit()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
toggle.addEventListener('click', () => {
|
|
75
|
+
const isOpen = sharePreview.classList.toggle('is-commenting-open')
|
|
76
|
+
toggle.setAttribute('aria-pressed', String(isOpen))
|
|
77
|
+
|
|
78
|
+
if (isOpen) {
|
|
79
|
+
maybeInit()
|
|
80
|
+
requestAnimationFrame(() => {
|
|
81
|
+
const postsContainer = sharePreview.querySelector<HTMLElement>(
|
|
82
|
+
'.Conversation-posts',
|
|
83
|
+
)
|
|
84
|
+
if (postsContainer) {
|
|
85
|
+
postsContainer.scrollTop = postsContainer.scrollHeight
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
onFind('.SharePreview-panel-close', (closeButton: HTMLElement) => {
|
|
93
|
+
const sharePreview = closeButton.closest<HTMLElement>('.SharePreview')
|
|
94
|
+
if (!sharePreview) return
|
|
95
|
+
|
|
96
|
+
closeButton.addEventListener('click', () => {
|
|
97
|
+
sharePreview.classList.remove('is-commenting-open')
|
|
98
|
+
const toggle = sharePreview.querySelector<HTMLElement>(
|
|
99
|
+
'.SharePreview-toggle',
|
|
100
|
+
)
|
|
101
|
+
toggle?.setAttribute('aria-pressed', 'false')
|
|
102
|
+
toggle?.focus()
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// When opened via a Shared Preview comment link (commentingOpen=true + #postId hash),
|
|
107
|
+
// wait for the conversation to load then scroll the target post into view.
|
|
108
|
+
onFind('.SharePreview.is-commenting-open', () => {
|
|
109
|
+
const hash = window.location.hash.slice(1)
|
|
110
|
+
if (!hash) return
|
|
111
|
+
|
|
112
|
+
// Guard against CSS selector injection — post IDs are UUIDs (hex + hyphens only)
|
|
113
|
+
if (!/^[a-f0-9-]+$/i.test(hash)) return
|
|
114
|
+
|
|
115
|
+
onFind(`[id="${hash}"]`, (post: HTMLElement) => {
|
|
116
|
+
post.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// ─── Pin overlay setup (lazy, called on first toggle open) ───────────────────
|
|
121
|
+
//
|
|
122
|
+
// The preview iframe may be cross-origin, so we cannot read its scroll position
|
|
123
|
+
// or intercept pointer events inside it. A same-origin click overlay sits above
|
|
124
|
+
// it to capture wheel/click, and PageFilter injects a script into the preview
|
|
125
|
+
// page that postMessages its scroll position to us.
|
|
126
|
+
|
|
127
|
+
function initCommentingOverlay(sharePreview: HTMLElement) {
|
|
128
|
+
const iframeEl = sharePreview.querySelector<HTMLIFrameElement>(
|
|
129
|
+
'iframe[name^="PreviewFrame-preview"]',
|
|
130
|
+
)
|
|
131
|
+
if (!iframeEl) return
|
|
132
|
+
const iframe: HTMLIFrameElement = iframeEl
|
|
133
|
+
|
|
134
|
+
const overlayEl = sharePreview.querySelector<HTMLElement>(
|
|
135
|
+
'.SharePreview-pinsOverlay',
|
|
136
|
+
)
|
|
137
|
+
if (!overlayEl) return
|
|
138
|
+
const overlay: HTMLElement = overlayEl
|
|
139
|
+
|
|
140
|
+
let cachedPins: PinData[] = []
|
|
141
|
+
|
|
142
|
+
// previewId from the page URL — always available on /preview/share-view?previewId=...
|
|
143
|
+
const urlPreviewId =
|
|
144
|
+
new URLSearchParams(window.location.search).get('previewId') || ''
|
|
145
|
+
|
|
146
|
+
// Scroll state of the preview document, reported by the injected script via
|
|
147
|
+
// postMessage. Pin coordinates are stored as fractions of the document size, so
|
|
148
|
+
// we wait for the first real ScrollData (hasScrollData) before rendering to
|
|
149
|
+
// avoid scaling against the wrong dimensions.
|
|
150
|
+
let postMessageScrollX = 0
|
|
151
|
+
let postMessageScrollY = 0
|
|
152
|
+
let postMessageDocWidth = 0
|
|
153
|
+
let postMessageDocHeight = 0
|
|
154
|
+
let hasScrollData = false
|
|
155
|
+
let pendingClick: PendingClick | null = null
|
|
156
|
+
|
|
157
|
+
// Resolve the preview origin once — iframe.src is stable.
|
|
158
|
+
let iframeOrigin = '*'
|
|
159
|
+
try {
|
|
160
|
+
iframeOrigin = new URL(iframe.src).origin
|
|
161
|
+
} catch {
|
|
162
|
+
/* keep '*' */
|
|
163
|
+
}
|
|
164
|
+
const postToPreview = (message: unknown) =>
|
|
165
|
+
iframe.contentWindow?.postMessage(message, iframeOrigin)
|
|
166
|
+
|
|
167
|
+
// Pin position helpers — single source of truth for the coordinate model.
|
|
168
|
+
const pinAbsoluteX = (pin: PinData) =>
|
|
169
|
+
(pin.xPercent / 100) * postMessageDocWidth
|
|
170
|
+
const pinAbsoluteY = (pin: PinData) =>
|
|
171
|
+
(pin.yPercent / 100) * postMessageDocHeight
|
|
172
|
+
const pinLeftPercent = (pin: PinData) =>
|
|
173
|
+
((pinAbsoluteX(pin) - postMessageScrollX) / (overlay.clientWidth || 1)) *
|
|
174
|
+
100
|
|
175
|
+
const pinTopPercent = (pin: PinData) =>
|
|
176
|
+
((pinAbsoluteY(pin) - postMessageScrollY) / (overlay.clientHeight || 1)) *
|
|
177
|
+
100
|
|
178
|
+
|
|
179
|
+
const setActivePin = (postId: string | null) => {
|
|
180
|
+
overlay.querySelectorAll<HTMLElement>('.SharePreview-pin').forEach((p) => {
|
|
181
|
+
p.classList.toggle('is-active', !!postId && p.dataset.postId === postId)
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const messageHandler = (e: MessageEvent) => {
|
|
186
|
+
if (!e.data || e.data.type !== 'BSP.SharedPreview.ScrollData') return
|
|
187
|
+
if (e.source !== iframe.contentWindow) return
|
|
188
|
+
const firstMessage = !hasScrollData
|
|
189
|
+
postMessageScrollX = e.data.scrollX as number
|
|
190
|
+
postMessageScrollY = e.data.scrollY as number
|
|
191
|
+
postMessageDocWidth = e.data.docWidth as number
|
|
192
|
+
postMessageDocHeight = e.data.docHeight as number
|
|
193
|
+
hasScrollData = true
|
|
194
|
+
// First message: need a full render to create pin elements.
|
|
195
|
+
// Subsequent scroll events: just reposition existing elements.
|
|
196
|
+
if (firstMessage) {
|
|
197
|
+
renderPins(cachedPins)
|
|
198
|
+
flushPendingClick()
|
|
199
|
+
} else {
|
|
200
|
+
repositionPins()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// This and the rtc.receive subscription and onFind('.Conversation-post') below
|
|
204
|
+
// are page-level and intentionally never torn down: a share-view page hosts a
|
|
205
|
+
// single .SharePreview for its lifetime, so initCommentingOverlay runs once.
|
|
206
|
+
window.addEventListener('message', messageHandler)
|
|
207
|
+
|
|
208
|
+
function getConversationEl(): HTMLElement | null {
|
|
209
|
+
return sharePreview.querySelector<HTMLElement>(
|
|
210
|
+
'.Conversation.frame[data-preview-id]',
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const resolvePreviewId = (): string =>
|
|
215
|
+
getConversationEl()?.dataset.previewId || urlPreviewId
|
|
216
|
+
|
|
217
|
+
// Current scroll state, used for pin placement. Every caller runs after the first ScrollData
|
|
218
|
+
// has arrived (hasScrollData === true), so this just returns the postMessage-backed state.
|
|
219
|
+
// The same-origin/overlay seeding for the script-blocked case lives in ensureScrollData().
|
|
220
|
+
function getScrollInfo(): ScrollState {
|
|
221
|
+
return {
|
|
222
|
+
scrollX: postMessageScrollX,
|
|
223
|
+
scrollY: postMessageScrollY,
|
|
224
|
+
docWidth: postMessageDocWidth,
|
|
225
|
+
docHeight: postMessageDocHeight,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Fallback when the injected ScrollData script never reports (blocked/absent): seed scroll state
|
|
230
|
+
// from a same-origin read so pins render against true document coordinates, degrading to
|
|
231
|
+
// overlay-relative dimensions only when the preview is cross-origin.
|
|
232
|
+
function ensureScrollData() {
|
|
233
|
+
if (hasScrollData) return
|
|
234
|
+
let scrollX = 0
|
|
235
|
+
let scrollY = 0
|
|
236
|
+
let docWidth = overlay.clientWidth || 1
|
|
237
|
+
let docHeight = overlay.clientHeight || 1
|
|
238
|
+
try {
|
|
239
|
+
const win = iframe.contentWindow
|
|
240
|
+
const doc = iframe.contentDocument
|
|
241
|
+
if (win && doc) {
|
|
242
|
+
scrollX = win.scrollX
|
|
243
|
+
scrollY = win.scrollY
|
|
244
|
+
docWidth = doc.documentElement.scrollWidth || docWidth
|
|
245
|
+
docHeight = doc.documentElement.scrollHeight || docHeight
|
|
246
|
+
}
|
|
247
|
+
} catch {
|
|
248
|
+
/* cross-origin — keep overlay-relative defaults */
|
|
249
|
+
}
|
|
250
|
+
postMessageScrollX = scrollX
|
|
251
|
+
postMessageScrollY = scrollY
|
|
252
|
+
postMessageDocWidth = docWidth
|
|
253
|
+
postMessageDocHeight = docHeight
|
|
254
|
+
hasScrollData = true
|
|
255
|
+
renderPins(cachedPins)
|
|
256
|
+
flushPendingClick()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function flushPendingClick() {
|
|
260
|
+
if (!pendingClick) return
|
|
261
|
+
const pc = pendingClick
|
|
262
|
+
pendingClick = null
|
|
263
|
+
const { scrollX, scrollY, docWidth, docHeight } = getScrollInfo()
|
|
264
|
+
const xPercent =
|
|
265
|
+
((scrollX + (pc.clientX - pc.overlayRect.left)) / docWidth) * 100
|
|
266
|
+
const yPercent =
|
|
267
|
+
((scrollY + (pc.clientY - pc.overlayRect.top)) / docHeight) * 100
|
|
268
|
+
closeActivePinPopover?.()
|
|
269
|
+
closeActivePinPopover = showPinPopover({
|
|
270
|
+
clientX: pc.clientX,
|
|
271
|
+
clientY: pc.clientY,
|
|
272
|
+
xPercent,
|
|
273
|
+
yPercent,
|
|
274
|
+
contentId: pc.contentId,
|
|
275
|
+
overlayId: pc.overlayId,
|
|
276
|
+
previewId: pc.previewId,
|
|
277
|
+
overlay,
|
|
278
|
+
onSuccess: fetchAndRenderPins,
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Full re-render: recreates pin DOM elements. Call only when the pin set changes
|
|
283
|
+
// (initial fetch, new/deleted pin via RTC). Use repositionPins() for scroll updates.
|
|
284
|
+
function renderPins(pins: PinData[]) {
|
|
285
|
+
cachedPins = pins
|
|
286
|
+
if (!hasScrollData) return
|
|
287
|
+
|
|
288
|
+
overlay.querySelectorAll('.SharePreview-pin').forEach((p) => p.remove())
|
|
289
|
+
|
|
290
|
+
pins.forEach((pin) => {
|
|
291
|
+
const pinEl = document.createElement('button')
|
|
292
|
+
pinEl.className = `SharePreview-pin SharePreview-pin--color${pin.colorIndex}`
|
|
293
|
+
pinEl.dataset.postId = pin.id
|
|
294
|
+
pinEl.textContent = pin.initials
|
|
295
|
+
pinEl.type = 'button'
|
|
296
|
+
pinEl.setAttribute('aria-label', `Comment by ${pin.initials}`)
|
|
297
|
+
|
|
298
|
+
pinEl.style.left = `${pinLeftPercent(pin)}%`
|
|
299
|
+
pinEl.style.top = `${pinTopPercent(pin)}%`
|
|
300
|
+
|
|
301
|
+
pinEl.addEventListener('click', (e) => {
|
|
302
|
+
e.stopPropagation()
|
|
303
|
+
highlightPost(pin.id)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
overlay.appendChild(pinEl)
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Lightweight scroll update: updates only style.left/top on existing pin elements.
|
|
311
|
+
function repositionPins() {
|
|
312
|
+
if (!hasScrollData) return
|
|
313
|
+
overlay
|
|
314
|
+
.querySelectorAll<HTMLElement>('.SharePreview-pin')
|
|
315
|
+
.forEach((pinEl) => {
|
|
316
|
+
const pin = cachedPins.find((p) => p.id === pinEl.dataset.postId)
|
|
317
|
+
if (!pin) return
|
|
318
|
+
pinEl.style.left = `${pinLeftPercent(pin)}%`
|
|
319
|
+
pinEl.style.top = `${pinTopPercent(pin)}%`
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Coalesce reposition work to one update per frame so a burst of wheel/scroll
|
|
324
|
+
// events doesn't trigger a layout write per event.
|
|
325
|
+
let repositionScheduled = false
|
|
326
|
+
function scheduleReposition() {
|
|
327
|
+
if (repositionScheduled) return
|
|
328
|
+
repositionScheduled = true
|
|
329
|
+
requestAnimationFrame(() => {
|
|
330
|
+
repositionScheduled = false
|
|
331
|
+
repositionPins()
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function fetchAndRenderPins() {
|
|
336
|
+
const previewId = resolvePreviewId()
|
|
337
|
+
if (!previewId) return
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
const response = await fetch(
|
|
341
|
+
`/cms/preview/share-view-conversation?previewId=${encodeURIComponent(previewId)}&pinsOnly=true`,
|
|
342
|
+
{ credentials: 'include' },
|
|
343
|
+
)
|
|
344
|
+
if (!response.ok) return
|
|
345
|
+
const pins: PinData[] = await response.json()
|
|
346
|
+
renderPins(pins)
|
|
347
|
+
} catch {
|
|
348
|
+
// Network or parse error — silently ignore
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function highlightPost(postId: string) {
|
|
353
|
+
const post = document.getElementById(postId)
|
|
354
|
+
if (post) {
|
|
355
|
+
post.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
356
|
+
post.classList.add('is-pin-active')
|
|
357
|
+
setTimeout(() => post.classList.remove('is-pin-active'), 2000)
|
|
358
|
+
}
|
|
359
|
+
setActivePin(postId)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ── Click overlay iframe ──────────────────────────────────────────────────
|
|
363
|
+
// A same-origin click overlay sits above the cross-origin preview iframe and,
|
|
364
|
+
// when commenting is open, captures wheel (forwarded as ScrollBy) and click
|
|
365
|
+
// (opens the pin placement popover).
|
|
366
|
+
|
|
367
|
+
const clickOverlayEl = sharePreview.querySelector<HTMLElement>(
|
|
368
|
+
'.SharePreview-clickOverlay',
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
// Teardown for the currently-open pin popover, if any. Tracked so re-clicking
|
|
372
|
+
// the overlay closes the prior popover through its full close path.
|
|
373
|
+
let closeActivePinPopover: (() => void) | null = null
|
|
374
|
+
|
|
375
|
+
if (clickOverlayEl) {
|
|
376
|
+
clickOverlayEl.addEventListener(
|
|
377
|
+
'wheel',
|
|
378
|
+
(e: WheelEvent) => {
|
|
379
|
+
e.preventDefault()
|
|
380
|
+
// Optimistic: update local scroll for immediate pin repositioning.
|
|
381
|
+
postMessageScrollX = Math.max(0, postMessageScrollX + e.deltaX)
|
|
382
|
+
postMessageScrollY = Math.max(0, postMessageScrollY + e.deltaY)
|
|
383
|
+
scheduleReposition()
|
|
384
|
+
postToPreview({
|
|
385
|
+
type: 'BSP.SharedPreview.ScrollBy',
|
|
386
|
+
deltaY: e.deltaY,
|
|
387
|
+
deltaX: e.deltaX,
|
|
388
|
+
})
|
|
389
|
+
},
|
|
390
|
+
{ passive: false },
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
// Click → place a pin at the clicked position
|
|
394
|
+
clickOverlayEl.addEventListener('click', (e: MouseEvent) => {
|
|
395
|
+
const conv = getConversationEl()
|
|
396
|
+
const previewId = resolvePreviewId()
|
|
397
|
+
if (!previewId) return
|
|
398
|
+
|
|
399
|
+
const contentId = conv?.dataset.contentId || ''
|
|
400
|
+
const overlayId = conv?.dataset.overlayId || ''
|
|
401
|
+
|
|
402
|
+
// If real scroll dimensions haven't arrived yet, defer coordinate
|
|
403
|
+
// computation until the first ScrollData message. Sending a zero-delta
|
|
404
|
+
// ScrollBy prompts the preview iframe to report its state immediately so
|
|
405
|
+
// the wait is typically only a few frames rather than the full fallback
|
|
406
|
+
// timeout.
|
|
407
|
+
if (!hasScrollData) {
|
|
408
|
+
pendingClick = {
|
|
409
|
+
clientX: e.clientX,
|
|
410
|
+
clientY: e.clientY,
|
|
411
|
+
overlayRect: clickOverlayEl.getBoundingClientRect(),
|
|
412
|
+
previewId,
|
|
413
|
+
contentId,
|
|
414
|
+
overlayId,
|
|
415
|
+
}
|
|
416
|
+
postToPreview({
|
|
417
|
+
type: 'BSP.SharedPreview.ScrollBy',
|
|
418
|
+
deltaX: 0,
|
|
419
|
+
deltaY: 0,
|
|
420
|
+
})
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const overlayRect = clickOverlayEl.getBoundingClientRect()
|
|
425
|
+
const { scrollX, scrollY, docWidth, docHeight } = getScrollInfo()
|
|
426
|
+
const xPercent =
|
|
427
|
+
((scrollX + (e.clientX - overlayRect.left)) / docWidth) * 100
|
|
428
|
+
const yPercent =
|
|
429
|
+
((scrollY + (e.clientY - overlayRect.top)) / docHeight) * 100
|
|
430
|
+
|
|
431
|
+
closeActivePinPopover?.()
|
|
432
|
+
|
|
433
|
+
closeActivePinPopover = showPinPopover({
|
|
434
|
+
clientX: e.clientX,
|
|
435
|
+
clientY: e.clientY,
|
|
436
|
+
xPercent,
|
|
437
|
+
yPercent,
|
|
438
|
+
contentId,
|
|
439
|
+
overlayId,
|
|
440
|
+
previewId,
|
|
441
|
+
overlay,
|
|
442
|
+
onSuccess: fetchAndRenderPins,
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Fetch pins when conversation panel finishes loading
|
|
448
|
+
onFind('.Conversation.frame.loaded', (conversation: HTMLElement) => {
|
|
449
|
+
if (!sharePreview.contains(conversation)) return
|
|
450
|
+
if (!conversation.dataset.previewId) return
|
|
451
|
+
fetchAndRenderPins()
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
// Real-time: refresh pins when any post is added/deleted for this preview
|
|
455
|
+
rtc.receive('com.psddev.cms.conversation.PostBroadcast', (data: unknown) => {
|
|
456
|
+
const postData = data as PostBroadcastData
|
|
457
|
+
const conv = getConversationEl()
|
|
458
|
+
const contentId = conv?.dataset.contentId
|
|
459
|
+
if (contentId && postData.contentId === contentId) {
|
|
460
|
+
fetchAndRenderPins()
|
|
461
|
+
}
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
// Bidirectional: hovering a panel post highlights its pin;
|
|
465
|
+
// clicking a panel post scrolls the preview iframe to the pin's location.
|
|
466
|
+
onFind('.Conversation-post', (post: HTMLElement) => {
|
|
467
|
+
const postId = post.id
|
|
468
|
+
if (!postId) return
|
|
469
|
+
|
|
470
|
+
post.addEventListener('mouseenter', () => setActivePin(postId))
|
|
471
|
+
post.addEventListener('mouseleave', () => setActivePin(null))
|
|
472
|
+
|
|
473
|
+
post.addEventListener('click', (e: MouseEvent) => {
|
|
474
|
+
// Don't intercept clicks on interactive elements within the post
|
|
475
|
+
if ((e.target as HTMLElement).closest('a, button, textarea, input'))
|
|
476
|
+
return
|
|
477
|
+
|
|
478
|
+
const pin = cachedPins.find((p) => p.id === postId)
|
|
479
|
+
if (!pin || !hasScrollData) return
|
|
480
|
+
|
|
481
|
+
// Center the pin vertically in the viewport
|
|
482
|
+
const targetScrollY = Math.max(
|
|
483
|
+
0,
|
|
484
|
+
pinAbsoluteY(pin) - (overlay.clientHeight || 1) / 2,
|
|
485
|
+
)
|
|
486
|
+
postToPreview({ type: 'BSP.SharedPreview.ScrollTo', top: targetScrollY })
|
|
487
|
+
|
|
488
|
+
// Optimistic update so pins reposition immediately
|
|
489
|
+
postMessageScrollY = targetScrollY
|
|
490
|
+
repositionPins()
|
|
491
|
+
})
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
// When commenting is turned off (panel close button or toggle), tear down any open pin popover.
|
|
495
|
+
// The pins themselves are hidden via CSS while commenting is closed.
|
|
496
|
+
new MutationObserver(() => {
|
|
497
|
+
if (!sharePreview.classList.contains('is-commenting-open')) {
|
|
498
|
+
closeActivePinPopover?.()
|
|
499
|
+
closeActivePinPopover = null
|
|
500
|
+
}
|
|
501
|
+
}).observe(sharePreview, { attributes: true, attributeFilter: ['class'] })
|
|
502
|
+
|
|
503
|
+
// Render even if the preview never reports its scroll position.
|
|
504
|
+
setTimeout(ensureScrollData, SCROLL_DATA_FALLBACK_MS)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ─── Pin placement popover ───────────────────────────────────────────────────
|
|
508
|
+
|
|
509
|
+
function showPinPopover(options: PinPopoverOptions): () => void {
|
|
510
|
+
const {
|
|
511
|
+
clientX,
|
|
512
|
+
clientY,
|
|
513
|
+
xPercent,
|
|
514
|
+
yPercent,
|
|
515
|
+
contentId,
|
|
516
|
+
overlayId,
|
|
517
|
+
previewId,
|
|
518
|
+
overlay,
|
|
519
|
+
onSuccess,
|
|
520
|
+
} = options
|
|
521
|
+
|
|
522
|
+
// Placeholder pin — shown while the user types, removed on submit or cancel
|
|
523
|
+
const placeholder = document.createElement('div')
|
|
524
|
+
placeholder.className = 'SharePreview-pin SharePreview-pin--placeholder'
|
|
525
|
+
placeholder.setAttribute('aria-hidden', 'true')
|
|
526
|
+
const overlayRect = overlay.getBoundingClientRect()
|
|
527
|
+
placeholder.style.left = `${((clientX - overlayRect.left) / overlayRect.width) * 100}%`
|
|
528
|
+
placeholder.style.top = `${((clientY - overlayRect.top) / overlayRect.height) * 100}%`
|
|
529
|
+
overlay.appendChild(placeholder)
|
|
530
|
+
|
|
531
|
+
const popover = document.createElement('div')
|
|
532
|
+
popover.className = 'SharePreview-pinPopover'
|
|
533
|
+
popover.setAttribute('role', 'dialog')
|
|
534
|
+
popover.setAttribute('aria-label', 'Add a comment')
|
|
535
|
+
|
|
536
|
+
// Position is handled by Tether (below), anchored to the placeholder pin.
|
|
537
|
+
|
|
538
|
+
// Clone the server-rendered rich-text editor template (formatting + @mentions); fall back to a plain textarea.
|
|
539
|
+
const editorTemplate = document.getElementById('sharePreview-pinEditor')
|
|
540
|
+
let editorEl: HTMLElement
|
|
541
|
+
let getValue: () => string
|
|
542
|
+
let focusEditor: () => void
|
|
543
|
+
let rteCleanup: (() => void) | null = null
|
|
544
|
+
|
|
545
|
+
if (editorTemplate instanceof HTMLTemplateElement) {
|
|
546
|
+
editorEl = document.createElement('div')
|
|
547
|
+
editorEl.className = 'SharePreview-pinPopover-editor'
|
|
548
|
+
editorEl.appendChild(editorTemplate.content.cloneNode(true))
|
|
549
|
+
const textarea = editorEl.querySelector<HTMLTextAreaElement>(
|
|
550
|
+
'textarea[name="text"]',
|
|
551
|
+
)
|
|
552
|
+
// Drop the cloned id so it can't collide with the panel's own editor.
|
|
553
|
+
textarea?.removeAttribute('id')
|
|
554
|
+
getValue = () => textarea?.value.trim() ?? ''
|
|
555
|
+
focusEditor = () =>
|
|
556
|
+
(editorEl.querySelector<HTMLElement>('.ProseMirror') ?? textarea)?.focus()
|
|
557
|
+
// Focus deterministically once the editor finishes enhancing, rather than racing the
|
|
558
|
+
// global .richtext watcher; cleaned up in close() if it never fires.
|
|
559
|
+
if (textarea) {
|
|
560
|
+
rteCleanup = onRTEReady((rte) => rte.view.dom.focus(), textarea)
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
const textarea = document.createElement('textarea')
|
|
564
|
+
textarea.className = 'SharePreview-pinPopover-input'
|
|
565
|
+
textarea.placeholder = 'Add a comment…'
|
|
566
|
+
textarea.rows = 3
|
|
567
|
+
editorEl = textarea
|
|
568
|
+
getValue = () => textarea.value.trim()
|
|
569
|
+
focusEditor = () => textarea.focus()
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const actions = document.createElement('div')
|
|
573
|
+
actions.className = 'SharePreview-pinPopover-actions'
|
|
574
|
+
|
|
575
|
+
const cancelBtn = document.createElement('button')
|
|
576
|
+
cancelBtn.type = 'button'
|
|
577
|
+
cancelBtn.className = 'SharePreview-pinPopover-cancel Button Button--border'
|
|
578
|
+
cancelBtn.textContent = 'Cancel'
|
|
579
|
+
|
|
580
|
+
const submitBtn = document.createElement('button')
|
|
581
|
+
submitBtn.type = 'button'
|
|
582
|
+
submitBtn.className = 'SharePreview-pinPopover-submit Button'
|
|
583
|
+
submitBtn.textContent = 'Post'
|
|
584
|
+
|
|
585
|
+
actions.append(cancelBtn, submitBtn)
|
|
586
|
+
popover.append(editorEl, actions)
|
|
587
|
+
document.body.appendChild(popover)
|
|
588
|
+
// Tether the popover to the placeholder pin: RTL-aware positioning that tracks scroll/resize.
|
|
589
|
+
// The plain constructor positions only (no outside-click close), so the @mention dropdown can
|
|
590
|
+
// tether outside the popover without dismissing it.
|
|
591
|
+
const tether = new Tether(placeholder, popover)
|
|
592
|
+
focusEditor()
|
|
593
|
+
|
|
594
|
+
// Dismiss is Cancel/Escape/placing another pin only — no outside-click close, since the
|
|
595
|
+
// @mention combo tethers its dropdown outside the popover and would dismiss it mid-mention.
|
|
596
|
+
let closed = false
|
|
597
|
+
const close = () => {
|
|
598
|
+
if (closed) return
|
|
599
|
+
closed = true
|
|
600
|
+
rteCleanup?.()
|
|
601
|
+
tether.destroy()
|
|
602
|
+
popover.remove()
|
|
603
|
+
placeholder.remove()
|
|
604
|
+
document.removeEventListener('keydown', escHandler)
|
|
605
|
+
}
|
|
606
|
+
const escHandler = (e: KeyboardEvent) => {
|
|
607
|
+
if (e.key === 'Escape') close()
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
cancelBtn.addEventListener('click', close)
|
|
611
|
+
|
|
612
|
+
submitBtn.addEventListener('click', async () => {
|
|
613
|
+
const text = getValue()
|
|
614
|
+
if (!text) {
|
|
615
|
+
focusEditor()
|
|
616
|
+
return
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
submitBtn.disabled = true
|
|
620
|
+
|
|
621
|
+
const headers = new Headers()
|
|
622
|
+
const cookie = Cookies.get('bsp.csrf')
|
|
623
|
+
if (cookie) headers.append('Brightspot-CSRF', cookie)
|
|
624
|
+
|
|
625
|
+
const body = new FormData()
|
|
626
|
+
body.append('postText', text)
|
|
627
|
+
body.append('contentId', contentId)
|
|
628
|
+
body.append('overlayId', overlayId)
|
|
629
|
+
body.append('sharedPreviewId', previewId)
|
|
630
|
+
body.append('xPercent', String(xPercent))
|
|
631
|
+
body.append('yPercent', String(yPercent))
|
|
632
|
+
body.append('postsOnly', 'true')
|
|
633
|
+
|
|
634
|
+
try {
|
|
635
|
+
const response = await fetch(
|
|
636
|
+
window.BRIGHTSPOT?.web?.pagePaths?.[
|
|
637
|
+
'com.psddev.cms.conversation.Posts'
|
|
638
|
+
],
|
|
639
|
+
{ method: 'post', credentials: 'include', headers, body },
|
|
640
|
+
)
|
|
641
|
+
// fetch only rejects on network errors, so a 4xx/5xx (e.g. invalid/expired
|
|
642
|
+
// one-time token, or auth required) would otherwise close the popover as if
|
|
643
|
+
// the comment saved and drop the user's text. Keep the popover open instead.
|
|
644
|
+
if (!response.ok) {
|
|
645
|
+
submitBtn.disabled = false
|
|
646
|
+
return
|
|
647
|
+
}
|
|
648
|
+
close()
|
|
649
|
+
onSuccess()
|
|
650
|
+
} catch {
|
|
651
|
+
submitBtn.disabled = false
|
|
652
|
+
}
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
document.addEventListener('keydown', escHandler)
|
|
656
|
+
|
|
657
|
+
// Return the teardown so callers close via the single path (element + listener + placeholder).
|
|
658
|
+
return close
|
|
659
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkbrightspot_cms=self.webpackChunkbrightspot_cms||[]).push([[1888],{95628(t,a,e){function o(t,a){return t?(Array.isArray(a)&&(a=a.join(",")),t.querySelector(a)):null}e.d(a,{A:()=>o}),Object.defineProperty(o,"name",{value:"default",configurable:!0})},60021(t,a,e){function o(t,a){return t?(Array.isArray(a)&&(a=a.join(",")),t.querySelector(a)):null}e.d(a,{A:()=>o}),Object.defineProperty(o,"name",{value:"default",configurable:!0})},83814(t,a,e){e.d(a,{default:()=>l}),e(84978),e(91672),e(92191),e(68690),e(45299);var o=e(53608),r=e.n(o),n=e(80907);if(7717==e.j)var i=e(95628);class l{constructor(t){if(this._container=t,this._latInput=(0,i.A)(t,".locationMapLatitude"),this._lngInput=(0,i.A)(t,".locationMapLongitude"),this._zoomInput=(0,i.A)(t,".locationMapZoom"),!this._latInput||!this._lngInput||!this._zoomInput)return;const{tileUrl:a,tileAttribution:e,geocoderSearchUrl:o,geocoderReverseUrl:l}=t.dataset,s=this._latInput.value,p=this._lngInput.value,c=""!==s&&""!==p,u=c?new(r().LatLng)(parseFloat(s),parseFloat(p)):new(r().LatLng)(38.95498,-77.365057);this._map=r().map(t,{center:u,zoom:parseFloat(this._zoomInput.value)||6,layers:[r().tileLayer(a||"https://tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:e||'© <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',referrerPolicy:"strict-origin-when-cross-origin"})]}),this._map.on("click",t=>{t.originalEvent.srcElement.classList.contains("locationMap")&&this.updateMarker(t.latlng)}),this._map.on("zoomend",()=>{this._zoomInput.value=this._map.getZoom()}),c&&this.updateMarker(u,""),new n.Vn({provider:new n.F6({...o&&{searchUrl:o},...l&&{reverseUrl:l}}),retainZoomLevel:!0,showMarker:!1}).addTo(this._map),this._map.on("geosearch/showlocation",t=>{t.location&&this.updateMarker(new(r().LatLng)(parseFloat(t.location.y),parseFloat(t.location.x)),t.location.label)}),r().control.locate({drawCircle:!1,markerStyle:{weight:2,radius:6},keepCurrentZoomLevel:!0,icon:"LocationMap-locateControl",iconLoading:"LocationMap-locateControl is-loading",strings:{title:"Set marker to your current location",popup:"<b>Current location</b><br/>(within {distance} {unit})"}}).addTo(this._map),this._map.on("locationfound",t=>{this.updateMarker(t.latlng,"")}),new(r().Control.extend({onAdd:()=>{const t=r().DomUtil.create("div");t.className="leaflet-bar leaflet-control";const a=r().DomUtil.create("a");return a.className="LocationMap-removeMarker",a.onclick=t=>{t.preventDefault(),t.stopPropagation(),this._marker&&(this._map.removeLayer(this._marker),this._marker=null,this._latInput.value=null,this._lngInput.value=null,this._container.classList.remove("is-marked"))},t.appendChild(a),t}}))({position:"topleft"}).addTo(this._map)}updateValue(t){this._latInput.value=t.lat,this._lngInput.value=t.lng}updateMarker(t,a){if(this._marker)this._marker.setLatLng(t),this._marker.bindPopup(a||t.toString());else{const e=r().divIcon({className:"LocationMap-marker",iconSize:[36,36],iconAnchor:[18,36],popupAnchor:[0,-30]});this._marker=r().marker(t,{draggable:!0,autoPan:!0,icon:e}).bindPopup(a||t.toString()),this._marker.on("moveend",t=>{this.updateValue(t.target._latlng),this._marker.bindPopup(t.target._latlng.toString())}),this._marker.addTo(this._map)}this._container.classList.add("is-marked"),this.updateValue(t)}}},48207(t,a,e){e.d(a,{default:()=>l}),e(49371),e(56065),e(79382),e(81499),e(80489);var o=e(66417),r=e.n(o),n=e(68098);if(3324==e.j)var i=e(60021);class l{constructor(t){if(this._container=t,this._latInput=(0,i.A)(t,".locationMapLatitude"),this._lngInput=(0,i.A)(t,".locationMapLongitude"),this._zoomInput=(0,i.A)(t,".locationMapZoom"),!this._latInput||!this._lngInput||!this._zoomInput)return;const{tileUrl:a,tileAttribution:e,geocoderSearchUrl:o,geocoderReverseUrl:l}=t.dataset,s=this._latInput.value,p=this._lngInput.value,c=""!==s&&""!==p,u=c?new(r().LatLng)(parseFloat(s),parseFloat(p)):new(r().LatLng)(38.95498,-77.365057);this._map=r().map(t,{center:u,zoom:parseFloat(this._zoomInput.value)||6,layers:[r().tileLayer(a||"https://tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:e||'© <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',referrerPolicy:"strict-origin-when-cross-origin"})]}),this._map.on("click",t=>{t.originalEvent.srcElement.classList.contains("locationMap")&&this.updateMarker(t.latlng)}),this._map.on("zoomend",()=>{this._zoomInput.value=this._map.getZoom()}),c&&this.updateMarker(u,""),new n.Vn({provider:new n.F6({...o&&{searchUrl:o},...l&&{reverseUrl:l}}),retainZoomLevel:!0,showMarker:!1}).addTo(this._map),this._map.on("geosearch/showlocation",t=>{t.location&&this.updateMarker(new(r().LatLng)(parseFloat(t.location.y),parseFloat(t.location.x)),t.location.label)}),r().control.locate({drawCircle:!1,markerStyle:{weight:2,radius:6},keepCurrentZoomLevel:!0,icon:"LocationMap-locateControl",iconLoading:"LocationMap-locateControl is-loading",strings:{title:"Set marker to your current location",popup:"<b>Current location</b><br/>(within {distance} {unit})"}}).addTo(this._map),this._map.on("locationfound",t=>{this.updateMarker(t.latlng,"")}),new(r().Control.extend({onAdd:()=>{const t=r().DomUtil.create("div");t.className="leaflet-bar leaflet-control";const a=r().DomUtil.create("a");return a.className="LocationMap-removeMarker",a.onclick=t=>{t.preventDefault(),t.stopPropagation(),this._marker&&(this._map.removeLayer(this._marker),this._marker=null,this._latInput.value=null,this._lngInput.value=null,this._container.classList.remove("is-marked"))},t.appendChild(a),t}}))({position:"topleft"}).addTo(this._map)}updateValue(t){this._latInput.value=t.lat,this._lngInput.value=t.lng}updateMarker(t,a){if(this._marker)this._marker.setLatLng(t),this._marker.bindPopup(a||t.toString());else{const e=r().divIcon({className:"LocationMap-marker",iconSize:[36,36],iconAnchor:[18,36],popupAnchor:[0,-30]});this._marker=r().marker(t,{draggable:!0,autoPan:!0,icon:e}).bindPopup(a||t.toString()),this._marker.on("moveend",t=>{this.updateValue(t.target._latlng),this._marker.bindPopup(t.target._latlng.toString())}),this._marker.addTo(this._map)}this._container.classList.add("is-marked"),this.updateValue(t)}}}}]);
|