@creativecreate/react-footnotes 1.0.0

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.
@@ -0,0 +1,155 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type FootnotesCategory = 'citation' | 'special';
5
+ type FootnoteKeys = string;
6
+ type FootnoteProps = {
7
+ id: FootnoteKeys;
8
+ type?: FootnotesCategory;
9
+ children?: ReactNode;
10
+ };
11
+ type FootnoteMessages = {
12
+ citation?: Record<string, ReactNode>;
13
+ special?: Record<string, ReactNode>;
14
+ };
15
+ type FootnoteContextType = {
16
+ registerFootnote: (footnote: FootnoteProps) => void;
17
+ citationList: FootnoteProps[];
18
+ specialList: FootnoteProps[];
19
+ clickHandler: (e: React.MouseEvent<HTMLButtonElement>) => void;
20
+ getContent: (id: string, type: FootnotesCategory) => ReactNode | null;
21
+ };
22
+ declare const splitFootnotes: (footnotes: FootnoteProps[]) => {
23
+ specialList: FootnoteProps[];
24
+ citationList: FootnoteProps[];
25
+ };
26
+ declare const getSpecialChar: (footnoteList: FootnoteProps[], id: FootnoteKeys) => string;
27
+
28
+ type FootnoteProviderProps = {
29
+ children: ReactNode;
30
+ /**
31
+ * Messages object containing footnote content.
32
+ * Structure: { citation: { id: content }, special: { id: content } }
33
+ */
34
+ messages?: FootnoteMessages;
35
+ /**
36
+ * Current pathname string for route-based footnote reset.
37
+ * For React Router: `\`${location.pathname}${location.search}\``
38
+ * For simple cases: `\`${window.location.pathname}${window.location.search}\``
39
+ */
40
+ pathname: string;
41
+ };
42
+ declare const FootnoteProvider: ({ children, messages, pathname, }: FootnoteProviderProps) => react_jsx_runtime.JSX.Element;
43
+ declare const useFootnotes: () => FootnoteContextType;
44
+
45
+ type FootnoteListProps = {
46
+ className?: string;
47
+ /**
48
+ * Optional className for the list item
49
+ */
50
+ itemClassName?: string;
51
+ /**
52
+ * Optional className for the <sup> element
53
+ */
54
+ itemSupClassName?: string;
55
+ /**
56
+ * Optional className for the content element
57
+ */
58
+ itemContentClassName?: string;
59
+ /**
60
+ * Optional className for the back link button
61
+ */
62
+ itemBackLinkClassName?: string;
63
+ /**
64
+ * Optional className for the back link icon
65
+ */
66
+ itemBackLinkIconClassName?: string;
67
+ /**
68
+ * Order of footnotes in the list
69
+ * @default 'special-first' - Special footnotes appear first, then citations
70
+ * 'citation-first' - Citations appear first, then special footnotes
71
+ */
72
+ order?: 'special-first' | 'citation-first';
73
+ };
74
+ declare const FootnoteList: ({ className, itemClassName, itemSupClassName, itemContentClassName, itemBackLinkClassName, itemBackLinkIconClassName, order, }: FootnoteListProps) => react_jsx_runtime.JSX.Element | null;
75
+
76
+ interface FootnoteListItemProps {
77
+ id: FootnoteKeys;
78
+ type?: 'citation' | 'special';
79
+ symbol: string;
80
+ children?: React.ReactNode;
81
+ /**
82
+ * Optional className to override default styles
83
+ */
84
+ className?: string;
85
+ /**
86
+ * Optional className for the <sup> element
87
+ */
88
+ supClassName?: string;
89
+ /**
90
+ * Optional className for the content element
91
+ */
92
+ contentClassName?: string;
93
+ /**
94
+ * Optional className for the back link button
95
+ */
96
+ backLinkClassName?: string;
97
+ /**
98
+ * Optional className for the back link icon
99
+ */
100
+ backLinkIconClassName?: string;
101
+ }
102
+ declare const FootnoteListItem: ({ id, children, symbol, className, supClassName, contentClassName, backLinkClassName, backLinkIconClassName, }: FootnoteListItemProps) => react_jsx_runtime.JSX.Element;
103
+
104
+ type FootnoteComponentProps = {
105
+ id: FootnoteKeys;
106
+ /**
107
+ * Type of footnote: 'citation' or 'special'
108
+ * Required when using messages from FootnoteProvider
109
+ */
110
+ type: FootnotesCategory;
111
+ /**
112
+ * Optional function to get footnote content.
113
+ * If provided, will override messages from FootnoteProvider.
114
+ * Should return the content for the footnote with the given id and type.
115
+ */
116
+ getContent?: (id: string, type: FootnotesCategory) => React.ReactNode;
117
+ /**
118
+ * Optional children to use as footnote content.
119
+ * If provided, will be used instead of getContent or messages.
120
+ */
121
+ children?: React.ReactNode;
122
+ /**
123
+ * Optional className to override default styles
124
+ */
125
+ className?: string;
126
+ /**
127
+ * Optional className for the <sup> element
128
+ */
129
+ supClassName?: string;
130
+ };
131
+
132
+ /**
133
+ * Footnote component - renders an inline footnote reference.
134
+ *
135
+ * Priority for content:
136
+ * 1. If `children` is provided, it will be used for the footnote content (highest priority).
137
+ * 2. If `getContent` prop is passed, it is called with `id` and `type` and its result is used.
138
+ * 3. Otherwise, content is fetched from messages via FootnoteProvider context (`getContentFromContext`).
139
+ *
140
+ * Registers itself in the footnote context via `registerFootnote` when content exists.
141
+ *
142
+ * Displays a symbol (number for 'citation', special character for 'special').
143
+ * Clicking the reference will navigate to its corresponding item in the FootnoteList.
144
+ *
145
+ * Props:
146
+ * - id: Unique id for the footnote
147
+ * - type: "citation" | "special"
148
+ * - getContent?: (id, type) => content (overrides messages)
149
+ * - children?: ReactNode (overrides everything, highest priority)
150
+ * - className?: class for the button/footnote ref
151
+ * - supClassName?: class for the <sup> element
152
+ */
153
+ declare const Footnote: ({ id, type, getContent, children, className, supClassName, }: FootnoteComponentProps) => react_jsx_runtime.JSX.Element | null;
154
+
155
+ export { Footnote, type FootnoteKeys, FootnoteList, FootnoteListItem, type FootnoteMessages, type FootnoteProps, FootnoteProvider, type FootnotesCategory, getSpecialChar, splitFootnotes, useFootnotes };
package/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/FootnoteProvider.tsx
7
+ var splitFootnotes = (footnotes) => {
8
+ const specialList = footnotes.filter(
9
+ (footnote) => footnote.type === "special"
10
+ );
11
+ const citationList = footnotes.filter(
12
+ (footnote) => footnote.type === "citation"
13
+ );
14
+ return {
15
+ specialList,
16
+ citationList
17
+ };
18
+ };
19
+ var getSpecialChar = (footnoteList, id) => {
20
+ const symbols = ["*", "\u2020", "\u2021", "\xA7", "\xB6", "#", "\u2016", "\u2042", "\u2051", "\u203B", "\u25CA", "\xA4"];
21
+ const specialFootnotes = footnoteList.filter((f) => f.type === "special");
22
+ const index = specialFootnotes.findIndex((f) => f.id === id);
23
+ return symbols[index] || "";
24
+ };
25
+ var FootnoteContext = react.createContext(
26
+ void 0
27
+ );
28
+ var FootnoteProvider = ({
29
+ children,
30
+ messages,
31
+ pathname
32
+ }) => {
33
+ const [footnotes, setFootnotes] = react.useState([]);
34
+ const activeClick = react.useRef(null);
35
+ const previousPathnameRef = react.useRef(null);
36
+ const getContent = react.useCallback((id, type) => {
37
+ if (!messages) return null;
38
+ if (type === "special" && messages.special?.[id]) {
39
+ return messages.special[id];
40
+ }
41
+ if (type === "citation" && messages.citation?.[id]) {
42
+ return messages.citation[id];
43
+ }
44
+ return null;
45
+ }, [messages]);
46
+ react.useEffect(() => {
47
+ if (previousPathnameRef.current !== null && previousPathnameRef.current !== pathname) {
48
+ setFootnotes([]);
49
+ }
50
+ previousPathnameRef.current = pathname;
51
+ }, [pathname]);
52
+ const registerFootnote = react.useCallback((footnote) => {
53
+ setFootnotes((prev) => {
54
+ const index = prev.findIndex(
55
+ (f) => f.id === footnote.id && f.type === footnote.type
56
+ );
57
+ if (index === -1) {
58
+ return [...prev, footnote];
59
+ }
60
+ return prev;
61
+ });
62
+ }, []);
63
+ const clickHandler = (e) => {
64
+ e.preventDefault();
65
+ const id = e.currentTarget.getAttribute("data-anchor") || "";
66
+ const isRefClick = id?.includes("footnote-");
67
+ let target = null;
68
+ if (isRefClick) {
69
+ activeClick.current = e.currentTarget;
70
+ }
71
+ if (!isRefClick && activeClick.current?.dataset?.id === id) {
72
+ target = activeClick.current;
73
+ activeClick.current = null;
74
+ } else if (!isRefClick) {
75
+ target = document.querySelector(`[data-id="${id}"]`);
76
+ activeClick.current = null;
77
+ } else {
78
+ target = document.getElementById(id);
79
+ }
80
+ if (target) {
81
+ const animationClass = ["underline", "animate-underline-flash"];
82
+ target.scrollIntoView({ behavior: "smooth" });
83
+ target.classList.remove(...animationClass);
84
+ void target.offsetWidth;
85
+ requestAnimationFrame(() => {
86
+ target.classList.add(...animationClass);
87
+ setTimeout(() => {
88
+ target.classList.remove(...animationClass);
89
+ }, 1500);
90
+ });
91
+ }
92
+ };
93
+ return /* @__PURE__ */ jsxRuntime.jsx(
94
+ FootnoteContext.Provider,
95
+ {
96
+ value: { registerFootnote, ...splitFootnotes(footnotes), clickHandler, getContent },
97
+ children
98
+ }
99
+ );
100
+ };
101
+ var useFootnotes = () => {
102
+ const context = react.useContext(FootnoteContext);
103
+ if (!context) {
104
+ throw new Error("useFootnotes must be used within a FootnoteProvider");
105
+ }
106
+ return context;
107
+ };
108
+ var FootnoteListItem = ({
109
+ id,
110
+ children,
111
+ symbol,
112
+ className,
113
+ supClassName,
114
+ contentClassName,
115
+ backLinkClassName,
116
+ backLinkIconClassName
117
+ }) => {
118
+ const { clickHandler } = useFootnotes();
119
+ const [hasReference, setHasReference] = react.useState(false);
120
+ react.useEffect(() => {
121
+ const checkReference = () => {
122
+ const reference = document.querySelector(`[data-id="ref-${id}"]`);
123
+ const isVisible = reference && !reference.closest(".hidden");
124
+ setHasReference(!!isVisible);
125
+ };
126
+ checkReference();
127
+ const mainElement = document.querySelector("main") || document.body;
128
+ const observer = new MutationObserver(checkReference);
129
+ observer.observe(mainElement, {
130
+ childList: true,
131
+ subtree: true
132
+ });
133
+ return () => observer.disconnect();
134
+ }, [id]);
135
+ const symbolId = `footnote-symbol-${id}`;
136
+ return /* @__PURE__ */ jsxRuntime.jsxs(
137
+ "li",
138
+ {
139
+ className: className || "footnote-list-item",
140
+ role: "doc-endnote",
141
+ children: [
142
+ /* @__PURE__ */ jsxRuntime.jsx(
143
+ "sup",
144
+ {
145
+ id: symbolId,
146
+ className: supClassName || "footnote-list-item__symbol",
147
+ "aria-label": `Footnote ${symbol}`,
148
+ children: symbol
149
+ }
150
+ ),
151
+ /* @__PURE__ */ jsxRuntime.jsx(
152
+ "span",
153
+ {
154
+ id: `footnote-${id}`,
155
+ className: contentClassName || "footnote-list-item__content",
156
+ "aria-labelledby": symbolId,
157
+ children
158
+ }
159
+ ),
160
+ " ",
161
+ hasReference && /* @__PURE__ */ jsxRuntime.jsx(
162
+ "button",
163
+ {
164
+ onClick: (e) => clickHandler(e),
165
+ "aria-label": `Back to reference ${symbol} in text`,
166
+ title: `Back to reference ${symbol}`,
167
+ "data-anchor": `ref-${id}`,
168
+ "data-testid": `back-to-reference-${id}`,
169
+ className: backLinkClassName || "footnote-list-item__back-link",
170
+ children: /* @__PURE__ */ jsxRuntime.jsx(
171
+ "small",
172
+ {
173
+ className: backLinkIconClassName || "footnote-list-item__back-link-icon",
174
+ "aria-hidden": "true",
175
+ children: "\u21A9"
176
+ }
177
+ )
178
+ }
179
+ )
180
+ ]
181
+ },
182
+ id
183
+ );
184
+ };
185
+ var FootnoteList = ({
186
+ className,
187
+ itemClassName,
188
+ itemSupClassName,
189
+ itemContentClassName,
190
+ itemBackLinkClassName,
191
+ itemBackLinkIconClassName,
192
+ order = "special-first"
193
+ }) => {
194
+ const { specialList, citationList } = useFootnotes();
195
+ if (!specialList.length && !citationList.length) return null;
196
+ const specialFootnotes = specialList.map((footnote) => ({
197
+ id: footnote.id,
198
+ children: footnote.children,
199
+ type: footnote.type,
200
+ symbol: getSpecialChar(specialList, footnote.id)
201
+ }));
202
+ const citationFootnotes = citationList.map((footnote, index) => ({
203
+ id: footnote.id,
204
+ children: footnote.children,
205
+ type: footnote.type,
206
+ symbol: `${index + 1}`
207
+ }));
208
+ const allFootnotes = order === "citation-first" ? [...citationFootnotes, ...specialFootnotes] : [...specialFootnotes, ...citationFootnotes];
209
+ return /* @__PURE__ */ jsxRuntime.jsx(
210
+ "ol",
211
+ {
212
+ className: className || "footnote-list",
213
+ role: "doc-endnotes",
214
+ "aria-label": "Footnotes",
215
+ children: allFootnotes.map((footnote) => /* @__PURE__ */ jsxRuntime.jsx(
216
+ FootnoteListItem,
217
+ {
218
+ id: footnote.id,
219
+ type: footnote.type,
220
+ symbol: footnote.symbol,
221
+ className: itemClassName,
222
+ supClassName: itemSupClassName,
223
+ contentClassName: itemContentClassName,
224
+ backLinkClassName: itemBackLinkClassName,
225
+ backLinkIconClassName: itemBackLinkIconClassName,
226
+ children: footnote.children
227
+ },
228
+ `footnote-${footnote.type}-${footnote.id}`
229
+ ))
230
+ }
231
+ );
232
+ };
233
+ var Footnote = ({
234
+ id,
235
+ type,
236
+ getContent,
237
+ children,
238
+ className,
239
+ supClassName
240
+ }) => {
241
+ const { registerFootnote, specialList, citationList, clickHandler, getContent: getContentFromContext } = useFootnotes();
242
+ let content = null;
243
+ if (children) {
244
+ content = children;
245
+ } else if (getContent) {
246
+ content = getContent(id, type);
247
+ } else if (getContentFromContext) {
248
+ content = getContentFromContext(id, type);
249
+ }
250
+ react.useEffect(() => {
251
+ if (type && content) {
252
+ registerFootnote({ id, children: content, type });
253
+ } else if (!content) {
254
+ console.warn(`Footnote ${id} has no content. Provide messages to FootnoteProvider, getContent function, or children.`);
255
+ }
256
+ }, [id, type, content, registerFootnote]);
257
+ if (!content) return null;
258
+ const specialSymbol = type === "special" ? getSpecialChar(specialList, id) : "";
259
+ const citationSymbol = type === "citation" ? citationList.findIndex((f) => f.id === id) : -1;
260
+ const symbol = type === "citation" ? `${citationSymbol + 1}` : specialSymbol;
261
+ return /* @__PURE__ */ jsxRuntime.jsx(
262
+ "button",
263
+ {
264
+ onClick: (e) => clickHandler(e),
265
+ "aria-label": `Footnote ${symbol}: See note ${symbol}`,
266
+ "aria-describedby": `footnote-${id}`,
267
+ role: "doc-noteref",
268
+ className: className || "footnote-ref",
269
+ "data-id": `ref-${id}`,
270
+ "data-anchor": `footnote-${id}`,
271
+ "data-testid": `footnote-${id}`,
272
+ children: /* @__PURE__ */ jsxRuntime.jsx("sup", { className: supClassName || "footnote-symbol", "aria-hidden": "true", children: symbol })
273
+ }
274
+ );
275
+ };
276
+
277
+ exports.Footnote = Footnote;
278
+ exports.FootnoteList = FootnoteList;
279
+ exports.FootnoteListItem = FootnoteListItem;
280
+ exports.FootnoteProvider = FootnoteProvider;
281
+ exports.getSpecialChar = getSpecialChar;
282
+ exports.splitFootnotes = splitFootnotes;
283
+ exports.useFootnotes = useFootnotes;
284
+ //# sourceMappingURL=index.js.map
285
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/FootnoteProvider.tsx","../src/FootnoteListItem.tsx","../src/FootnoteList.tsx","../src/Footnote.tsx"],"names":["createContext","useState","useRef","useCallback","useEffect","jsx","useContext","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["'use client';\nimport {\n createContext,\n useContext,\n useState,\n ReactNode,\n useEffect,\n useCallback,\n useRef,\n} from 'react';\n\nexport type FootnotesCategory = 'citation' | 'special';\nexport type FootnoteKeys = string;\nexport type FootnoteProps = {\n id: FootnoteKeys;\n type?: FootnotesCategory;\n children?: ReactNode;\n};\n\ntype FootnoteMessages = {\n citation?: Record<string, ReactNode>;\n special?: Record<string, ReactNode>;\n};\n\ntype FootnoteContextType = {\n registerFootnote: (footnote: FootnoteProps) => void;\n citationList: FootnoteProps[];\n specialList: FootnoteProps[];\n clickHandler: (e: React.MouseEvent<HTMLButtonElement>) => void;\n getContent: (id: string, type: FootnotesCategory) => ReactNode | null;\n};\n\nexport const splitFootnotes = (footnotes: FootnoteProps[]) => {\n const specialList = footnotes.filter(\n (footnote) => footnote.type === 'special',\n );\n const citationList = footnotes.filter(\n (footnote) => footnote.type === 'citation',\n );\n return {\n specialList,\n citationList,\n };\n};\n\nexport const getSpecialChar = (\n footnoteList: FootnoteProps[],\n id: FootnoteKeys,\n) => {\n const symbols = ['*', '†', '‡', '§', '¶', '#', '‖', '⁂', '⁑', '※', '◊', '¤'];\n // Get all previous special footnotes before this one\n const specialFootnotes = footnoteList.filter((f) => f.type === 'special');\n const index = specialFootnotes.findIndex((f) => f.id === id);\n return symbols[index] || '';\n};\n\nexport type { FootnoteMessages };\n\ntype FootnoteProviderProps = {\n children: ReactNode;\n /**\n * Messages object containing footnote content.\n * Structure: { citation: { id: content }, special: { id: content } }\n */\n messages?: FootnoteMessages;\n /**\n * Current pathname string for route-based footnote reset.\n * For React Router: `\\`${location.pathname}${location.search}\\``\n * For simple cases: `\\`${window.location.pathname}${window.location.search}\\``\n */\n pathname: string;\n};\n\nconst FootnoteContext = createContext<FootnoteContextType | undefined>(\n undefined,\n);\n\nexport const FootnoteProvider = ({ \n children,\n messages,\n pathname,\n}: FootnoteProviderProps) => {\n const [footnotes, setFootnotes] = useState<FootnoteProps[]>([]);\n const activeClick = useRef<HTMLButtonElement>(null);\n const previousPathnameRef = useRef<string | null>(null);\n\n // Function to get content from messages\n const getContent = useCallback((id: string, type: FootnotesCategory): ReactNode | null => {\n if (!messages) return null;\n \n if (type === 'special' && messages.special?.[id]) {\n return messages.special[id];\n }\n \n if (type === 'citation' && messages.citation?.[id]) {\n return messages.citation[id];\n }\n \n return null;\n }, [messages]);\n\n // Reset footnotes when route changes\n useEffect(() => {\n // Only reset if pathname actually changed (not on initial mount)\n if (previousPathnameRef.current !== null && previousPathnameRef.current !== pathname) {\n setFootnotes([]);\n }\n previousPathnameRef.current = pathname;\n }, [pathname]);\n\n // update footnotes\n const registerFootnote = useCallback((footnote: FootnoteProps) => {\n setFootnotes((prev) => {\n // Check if a footnote with the same id AND type already exists\n const index = prev.findIndex(\n (f) => f.id === footnote.id && f.type === footnote.type\n );\n // new (either new id or same id but different type)\n if (index === -1) {\n return [...prev, footnote];\n }\n // duplicate (same id and same type)\n return prev;\n });\n }, []);\n\n // handle all footnote click events\n // handle \"footnote <sup>\" clicks (isRefClick = true)\n // - only single target instance, no duplicated targets\n // - when clicking on a \"footnote ref\", navigate to the matching footnote list item\n // handle \"back to ref\" button clicks (isRefClick = false)\n // - can have duplicate targets\n // - when clicking on the \"back to ref\", track and point to the active ref target\n // - when clicking on a random \"back to ref\" link, navigate to the first instance\n const clickHandler = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault();\n\n // get the id from the data-anchor - 'ref-<id>' or 'footnote-<id>'\n const id = e.currentTarget.getAttribute('data-anchor') || '';\n const isRefClick = id?.includes('footnote-');\n let target: HTMLButtonElement | null = null;\n\n // set active ref\n if (isRefClick) {\n activeClick.current = e.currentTarget;\n }\n\n // handle \"back to ref\" link clicks. point to active ref\n if (!isRefClick && activeClick.current?.dataset?.id === id) {\n target = activeClick.current;\n activeClick.current = null;\n } else if (!isRefClick) {\n // handle random \"back to ref\" link clicks. point to first instance\n target = document.querySelector(`[data-id=\"${id}\"]`);\n activeClick.current = null;\n } else {\n // handle \"footnote <sup>\" clicks. point to target\n target = document.getElementById(id) as HTMLButtonElement;\n }\n\n if (target) {\n const animationClass = ['underline', 'animate-underline-flash'];\n // scroll to target\n target.scrollIntoView({ behavior: 'smooth' });\n // remove existing animation class\n target.classList.remove(...animationClass);\n // Force a reflow to ensure removal is processed\n void target.offsetWidth;\n // Schedule the class addition for the next animation frame\n requestAnimationFrame(() => {\n target.classList.add(...animationClass);\n // Remove classes after animation completes (0.6s to match CSS animation duration)\n setTimeout(() => {\n target.classList.remove(...animationClass);\n }, 1500);\n });\n }\n };\n\n return (\n <FootnoteContext.Provider\n value={{ registerFootnote, ...splitFootnotes(footnotes), clickHandler, getContent }}\n >\n {children}\n </FootnoteContext.Provider>\n );\n};\n\nexport const useFootnotes = () => {\n const context = useContext(FootnoteContext);\n if (!context) {\n throw new Error('useFootnotes must be used within a FootnoteProvider');\n }\n return context;\n};\n","'use client';\nimport { FootnoteKeys, useFootnotes } from './FootnoteProvider';\nimport { useEffect, useState } from 'react';\n\ninterface FootnoteListItemProps {\n id: FootnoteKeys;\n type?: 'citation' | 'special';\n symbol: string;\n children?: React.ReactNode;\n /**\n * Optional className to override default styles\n */\n className?: string;\n /**\n * Optional className for the <sup> element\n */\n supClassName?: string;\n /**\n * Optional className for the content element\n */\n contentClassName?: string;\n /**\n * Optional className for the back link button\n */\n backLinkClassName?: string;\n /**\n * Optional className for the back link icon\n */\n backLinkIconClassName?: string;\n}\n\nexport const FootnoteListItem = ({\n id,\n children,\n symbol,\n className,\n supClassName,\n contentClassName,\n backLinkClassName,\n backLinkIconClassName,\n}: FootnoteListItemProps) => {\n const { clickHandler } = useFootnotes();\n const [hasReference, setHasReference] = useState(false);\n\n // Check if the reference exists in the DOM\n useEffect(() => {\n const checkReference = () => {\n const reference = document.querySelector(`[data-id=\"ref-${id}\"]`);\n // if elm exists and none of its parents are hidden\n const isVisible = reference && !reference.closest('.hidden');\n setHasReference(!!isVisible);\n };\n\n // Initial check\n checkReference();\n\n // Set up a mutation observer to watch for DOM changes in the main element only\n const mainElement = document.querySelector('main') || document.body;\n const observer = new MutationObserver(checkReference);\n observer.observe(mainElement, {\n childList: true,\n subtree: true,\n });\n return () => observer.disconnect();\n }, [id]);\n\n const symbolId = `footnote-symbol-${id}`;\n\n return (\n <li\n className={className || 'footnote-list-item'}\n key={id}\n role=\"doc-endnote\"\n >\n {/* footnote reference */}\n <sup \n id={symbolId}\n className={supClassName || 'footnote-list-item__symbol'}\n aria-label={`Footnote ${symbol}`}\n >\n {symbol}\n </sup>\n <span\n id={`footnote-${id}`}\n className={contentClassName || 'footnote-list-item__content'}\n aria-labelledby={symbolId}\n >\n {children}\n </span>\n {' '}\n\n {/* back to reference link */}\n {hasReference && (\n <button\n onClick={(e) => clickHandler(e)}\n aria-label={`Back to reference ${symbol} in text`}\n title={`Back to reference ${symbol}`}\n data-anchor={`ref-${id}`}\n data-testid={`back-to-reference-${id}`}\n className={backLinkClassName || 'footnote-list-item__back-link'}\n >\n <small \n className={backLinkIconClassName || \"footnote-list-item__back-link-icon\"}\n aria-hidden=\"true\"\n >\n ↩\n </small>\n </button>\n )}\n </li>\n );\n};\n","'use client';\nimport {\n getSpecialChar,\n useFootnotes,\n} from './FootnoteProvider';\nimport { FootnoteListItem } from './FootnoteListItem';\n\ntype FootnoteListProps = {\n // Optional className for the wrapper\n className?: string;\n /**\n * Optional className for the list item\n */\n itemClassName?: string;\n /**\n * Optional className for the <sup> element\n */\n itemSupClassName?: string;\n /**\n * Optional className for the content element\n */\n itemContentClassName?: string;\n /**\n * Optional className for the back link button\n */\n itemBackLinkClassName?: string;\n /**\n * Optional className for the back link icon\n */\n itemBackLinkIconClassName?: string;\n /**\n * Order of footnotes in the list\n * @default 'special-first' - Special footnotes appear first, then citations\n * 'citation-first' - Citations appear first, then special footnotes\n */\n order?: 'special-first' | 'citation-first';\n};\n\nexport const FootnoteList = ({ \n className,\n itemClassName,\n itemSupClassName,\n itemContentClassName,\n itemBackLinkClassName,\n itemBackLinkIconClassName,\n order = 'special-first',\n}: FootnoteListProps) => {\n const { specialList, citationList } = useFootnotes();\n\n // exit if no footnotes\n if (!specialList.length && !citationList.length) return null;\n\n // Map footnotes with their symbols\n const specialFootnotes = specialList.map((footnote) => ({\n id: footnote.id,\n children: footnote.children,\n type: footnote.type,\n symbol: getSpecialChar(specialList, footnote.id),\n }));\n\n const citationFootnotes = citationList.map((footnote, index) => ({\n id: footnote.id,\n children: footnote.children,\n type: footnote.type,\n symbol: `${index + 1}`,\n }));\n\n // Combine footnotes based on order prop\n const allFootnotes = order === 'citation-first'\n ? [...citationFootnotes, ...specialFootnotes]\n : [...specialFootnotes, ...citationFootnotes];\n\n return (\n <ol \n className={className || \"footnote-list\"}\n role=\"doc-endnotes\"\n aria-label=\"Footnotes\"\n >\n {allFootnotes.map((footnote) => (\n <FootnoteListItem\n key={`footnote-${footnote.type}-${footnote.id}`}\n id={footnote.id}\n type={footnote.type}\n symbol={footnote.symbol}\n className={itemClassName}\n supClassName={itemSupClassName}\n contentClassName={itemContentClassName}\n backLinkClassName={itemBackLinkClassName}\n backLinkIconClassName={itemBackLinkIconClassName}\n >\n {footnote.children}\n </FootnoteListItem>\n ))}\n </ol>\n );\n};\n","'use client';\nimport { useEffect } from 'react';\nimport {\n useFootnotes,\n FootnoteKeys,\n FootnotesCategory,\n getSpecialChar,\n} from './FootnoteProvider';\n\ntype FootnoteComponentProps = {\n id: FootnoteKeys;\n /**\n * Type of footnote: 'citation' or 'special'\n * Required when using messages from FootnoteProvider\n */\n type: FootnotesCategory;\n /**\n * Optional function to get footnote content.\n * If provided, will override messages from FootnoteProvider.\n * Should return the content for the footnote with the given id and type.\n */\n getContent?: (id: string, type: FootnotesCategory) => React.ReactNode;\n /**\n * Optional children to use as footnote content.\n * If provided, will be used instead of getContent or messages.\n */\n children?: React.ReactNode;\n /**\n * Optional className to override default styles\n */\n className?: string;\n /**\n * Optional className for the <sup> element\n */\n supClassName?: string;\n};\n\n// Re-export for convenience\nexport type { FootnoteKeys } from './FootnoteProvider';\n\n/**\n * Footnote component - renders an inline footnote reference.\n * \n * Priority for content:\n * 1. If `children` is provided, it will be used for the footnote content (highest priority).\n * 2. If `getContent` prop is passed, it is called with `id` and `type` and its result is used.\n * 3. Otherwise, content is fetched from messages via FootnoteProvider context (`getContentFromContext`).\n * \n * Registers itself in the footnote context via `registerFootnote` when content exists.\n * \n * Displays a symbol (number for 'citation', special character for 'special').\n * Clicking the reference will navigate to its corresponding item in the FootnoteList.\n * \n * Props:\n * - id: Unique id for the footnote\n * - type: \"citation\" | \"special\"\n * - getContent?: (id, type) => content (overrides messages)\n * - children?: ReactNode (overrides everything, highest priority)\n * - className?: class for the button/footnote ref\n * - supClassName?: class for the <sup> element\n */\nexport const Footnote = ({ \n id, \n type,\n getContent, \n children,\n className,\n supClassName,\n}: FootnoteComponentProps) => {\n const { registerFootnote, specialList, citationList, clickHandler, getContent: getContentFromContext } =\n useFootnotes();\n\n let content: React.ReactNode = null;\n if (children) {\n content = children;\n } else if (getContent) {\n content = getContent(id, type);\n } else if (getContentFromContext) {\n content = getContentFromContext(id, type);\n }\n\n useEffect(() => {\n if (type && content) {\n registerFootnote({ id, children: content, type });\n } else if (!content) {\n console.warn(`Footnote ${id} has no content. Provide messages to FootnoteProvider, getContent function, or children.`);\n }\n }, [id, type, content, registerFootnote]);\n\n // exit if no content\n if (!content) return null;\n\n // create symbol\n const specialSymbol = type === 'special' ? getSpecialChar(specialList, id) : '';\n const citationSymbol = type === 'citation'\n ? citationList.findIndex((f) => f.id === id)\n : -1;\n const symbol = type === 'citation' ? `${citationSymbol + 1}` : specialSymbol;\n\n return (\n <button\n onClick={(e) => clickHandler(e)}\n aria-label={`Footnote ${symbol}: See note ${symbol}`}\n aria-describedby={`footnote-${id}`}\n role=\"doc-noteref\"\n className={className || 'footnote-ref'}\n data-id={`ref-${id}`}\n data-anchor={`footnote-${id}`}\n data-testid={`footnote-${id}`}\n >\n <sup className={supClassName || \"footnote-symbol\"} aria-hidden=\"true\">{symbol}</sup>\n </button>\n );\n};\n"]}