@mieweb/ui 0.7.3-dev.20 → 0.7.3-dev.22

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.
Files changed (56) hide show
  1. package/dist/brands/index.cjs +13 -13
  2. package/dist/brands/index.js +1 -1
  3. package/dist/chunk-5JZIDILK.cjs +164 -0
  4. package/dist/chunk-5JZIDILK.cjs.map +1 -0
  5. package/dist/chunk-7IKLGH3Z.cjs +92 -0
  6. package/dist/chunk-7IKLGH3Z.cjs.map +1 -0
  7. package/dist/chunk-7L7OPISJ.cjs +291 -0
  8. package/dist/chunk-7L7OPISJ.cjs.map +1 -0
  9. package/dist/{chunk-VNNFI5DQ.cjs → chunk-ABST3R3G.cjs} +60 -2
  10. package/dist/chunk-ABST3R3G.cjs.map +1 -0
  11. package/dist/{chunk-4PJ24GQ4.js → chunk-DX64KLSN.js} +2 -2
  12. package/dist/{chunk-4PJ24GQ4.js.map → chunk-DX64KLSN.js.map} +1 -1
  13. package/dist/chunk-JHZW3Y62.js +142 -0
  14. package/dist/chunk-JHZW3Y62.js.map +1 -0
  15. package/dist/{chunk-3Z7CLG4U.js → chunk-VCHTV65A.js} +60 -2
  16. package/dist/chunk-VCHTV65A.js.map +1 -0
  17. package/dist/chunk-WBGUYSWY.js +90 -0
  18. package/dist/chunk-WBGUYSWY.js.map +1 -0
  19. package/dist/{chunk-LGYGKLJL.cjs → chunk-WIYQI35R.cjs} +2 -2
  20. package/dist/{chunk-LGYGKLJL.cjs.map → chunk-WIYQI35R.cjs.map} +1 -1
  21. package/dist/{chunk-WG6MPHWK.cjs → chunk-XJTFZLWH.cjs} +2 -86
  22. package/dist/chunk-XJTFZLWH.cjs.map +1 -0
  23. package/dist/{chunk-KGS7BL5T.js → chunk-XUVEF2RQ.js} +4 -87
  24. package/dist/chunk-XUVEF2RQ.js.map +1 -0
  25. package/dist/chunk-XWYKLEKG.js +269 -0
  26. package/dist/chunk-XWYKLEKG.js.map +1 -0
  27. package/dist/components/GlossaryTooltip/index.cjs +15 -0
  28. package/dist/components/GlossaryTooltip/index.cjs.map +1 -0
  29. package/dist/components/GlossaryTooltip/index.d.cts +64 -0
  30. package/dist/components/GlossaryTooltip/index.d.ts +64 -0
  31. package/dist/components/GlossaryTooltip/index.js +6 -0
  32. package/dist/components/GlossaryTooltip/index.js.map +1 -0
  33. package/dist/components/Markdown/index.cjs +15 -15
  34. package/dist/components/Markdown/index.js +3 -3
  35. package/dist/components/SectionSpyNav/index.cjs +15 -0
  36. package/dist/components/SectionSpyNav/index.cjs.map +1 -0
  37. package/dist/components/SectionSpyNav/index.d.cts +63 -0
  38. package/dist/components/SectionSpyNav/index.d.ts +63 -0
  39. package/dist/components/SectionSpyNav/index.js +6 -0
  40. package/dist/components/SectionSpyNav/index.js.map +1 -0
  41. package/dist/hooks/index.cjs +18 -17
  42. package/dist/hooks/index.js +2 -1
  43. package/dist/index.cjs +161 -150
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +2 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +25 -21
  48. package/dist/index.js.map +1 -1
  49. package/dist/styles.css +1 -1
  50. package/dist/tailwind-preset.cjs +4 -4
  51. package/dist/tailwind-preset.js +1 -1
  52. package/package.json +1 -1
  53. package/dist/chunk-3Z7CLG4U.js.map +0 -1
  54. package/dist/chunk-KGS7BL5T.js.map +0 -1
  55. package/dist/chunk-VNNFI5DQ.cjs.map +0 -1
  56. package/dist/chunk-WG6MPHWK.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback, useRef } from 'react';
1
+ import { useState, useEffect, useCallback } from 'react';
2
2
 
3
3
  // src/hooks/useDirection.ts
4
4
  var RTL_LOCALES = ["ar", "he", "fa", "ur"];
@@ -124,90 +124,7 @@ function useIsLargeDesktop() {
124
124
  function useIsMobileOrTablet() {
125
125
  return useMediaQuery("(max-width: 1023px)");
126
126
  }
127
- function useScrollSpy({
128
- selectors,
129
- ids,
130
- rootMargin = "0px 0px -60% 0px",
131
- threshold = 0,
132
- root,
133
- enabled = true
134
- } = {}) {
135
- const [activeId, setActiveId] = useState(null);
136
- const visibleEntries = useRef(
137
- /* @__PURE__ */ new Map()
138
- );
139
- const rootEl = root?.current ?? null;
140
- useEffect(() => {
141
- if (!enabled) return;
142
- const container = rootEl ?? document;
143
- function resolveElements() {
144
- let els = [];
145
- if (ids && ids.length > 0) {
146
- els = ids.map((id) => document.getElementById(id)).filter((el) => el !== null);
147
- } else if (selectors) {
148
- els = Array.from(container.querySelectorAll(selectors));
149
- }
150
- return els.filter((el) => el.id);
151
- }
152
- let intersectionObserver = null;
153
- let mutationObserver = null;
154
- function startObserving() {
155
- const elements = resolveElements();
156
- if (elements.length === 0) return false;
157
- intersectionObserver = new IntersectionObserver(
158
- (entries2) => {
159
- for (const entry of entries2) {
160
- if (entry.isIntersecting) {
161
- visibleEntries.current.set(entry.target.id, entry);
162
- } else {
163
- visibleEntries.current.delete(entry.target.id);
164
- }
165
- }
166
- if (visibleEntries.current.size > 0) {
167
- let topmost = null;
168
- let topmostTop = Infinity;
169
- for (const [id, entry] of visibleEntries.current) {
170
- const top = entry.boundingClientRect.top;
171
- if (top < topmostTop) {
172
- topmostTop = top;
173
- topmost = id;
174
- }
175
- }
176
- if (topmost) {
177
- setActiveId(topmost);
178
- }
179
- }
180
- },
181
- {
182
- root: rootEl,
183
- rootMargin,
184
- threshold
185
- }
186
- );
187
- for (const el of elements) {
188
- intersectionObserver.observe(el);
189
- }
190
- mutationObserver?.disconnect();
191
- mutationObserver = null;
192
- return true;
193
- }
194
- if (!startObserving()) {
195
- const watchRoot = rootEl ?? document.body;
196
- mutationObserver = new MutationObserver(() => {
197
- startObserving();
198
- });
199
- mutationObserver.observe(watchRoot, { childList: true, subtree: true });
200
- }
201
- const entries = visibleEntries.current;
202
- return () => {
203
- mutationObserver?.disconnect();
204
- intersectionObserver?.disconnect();
205
- entries.clear();
206
- };
207
- }, [selectors, ids, rootMargin, threshold, rootEl, enabled]);
208
- return { activeId };
209
- }
210
127
 
211
- export { RTL_LOCALES, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardShortcut, useMediaQuery, useScrollSpy };
212
- //# sourceMappingURL=chunk-KGS7BL5T.js.map
213
- //# sourceMappingURL=chunk-KGS7BL5T.js.map
128
+ export { RTL_LOCALES, isRtlLocale, useCommandK, useDirection, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardShortcut, useMediaQuery };
129
+ //# sourceMappingURL=chunk-XUVEF2RQ.js.map
130
+ //# sourceMappingURL=chunk-XUVEF2RQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useDirection.ts","../src/hooks/useKeyboardShortcut.ts","../src/hooks/useMediaQuery.ts"],"names":["useEffect","useState"],"mappings":"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","file":"chunk-XUVEF2RQ.js","sourcesContent":["import { useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\n\nexport type Direction = 'ltr' | 'rtl';\n\n/** Locales whose primary script reads right-to-left. */\nexport const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'] as const;\n\n/**\n * Returns true when the given locale (e.g. `ar`, `ar-SA`, `he_IL`) uses a\n * right-to-left script.\n */\nexport function isRtlLocale(locale: string): boolean {\n const lang = locale.toLowerCase().split(/[-_]/)[0];\n return (RTL_LOCALES as readonly string[]).includes(lang);\n}\n\nfunction resolveDirection(element?: HTMLElement | null): Direction {\n if (typeof document === 'undefined') return 'ltr';\n const target = element ?? document.documentElement;\n return getComputedStyle(target).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/**\n * Hook returning the effective text direction (`ltr` | `rtl`) inherited from\n * the nearest `dir` attribute — zero setup for consumers who set\n * `<html dir=\"rtl\">`.\n *\n * Use for JS-level direction logic that CSS logical properties can't cover:\n * inverting ArrowLeft/ArrowRight keyboard handling, popover/toast placement,\n * drag math, etc.\n *\n * @param elementRef - Optional ref; when provided, direction is resolved from\n * that element (respecting any local `dir` override) instead of `<html>`.\n *\n * @example\n * ```tsx\n * const direction = useDirection();\n * const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n * ```\n */\nexport function useDirection(\n elementRef?: RefObject<HTMLElement | null>\n): Direction {\n const [direction, setDirection] = useState<Direction>(() =>\n resolveDirection(elementRef?.current)\n );\n\n useEffect(() => {\n const update = () => setDirection(resolveDirection(elementRef?.current));\n update();\n // dir changes are rare; observing the whole tree for just this attribute is cheap.\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['dir'],\n subtree: true,\n });\n return () => observer.disconnect();\n }, [elementRef]);\n\n // Re-sync when a re-render points elementRef.current at a different node:\n // the MutationObserver only fires on `dir` attribute mutations, so a swapped\n // ref target with a different inherited direction would otherwise go stale.\n const currentElement = elementRef?.current ?? null;\n useEffect(() => {\n setDirection(resolveDirection(currentElement));\n }, [currentElement]);\n\n return direction;\n}\n","import { useEffect, useCallback } from 'react';\n\n/**\n * Options for keyboard shortcut hook\n */\nexport interface KeyboardShortcutOptions {\n /** Whether the shortcut is currently enabled (default: true) */\n enabled?: boolean;\n /** Modifier keys required (default: none) */\n modifiers?: {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n };\n /** Whether to prevent default browser behavior (default: true) */\n preventDefault?: boolean;\n /** Whether to stop event propagation (default: false) */\n stopPropagation?: boolean;\n /** Element types to ignore when the shortcut is pressed (default: ['INPUT', 'TEXTAREA', 'SELECT']) */\n ignoreInputs?: boolean;\n}\n\n/**\n * Hook that triggers a callback when a specific keyboard shortcut is pressed.\n * Supports modifier keys (Ctrl, Shift, Alt, Meta) and ignores input fields by default.\n *\n * @param key - The key to listen for (e.g., 'k', 'Enter', 'Escape', '/')\n * @param callback - Function to call when the shortcut is pressed\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Cmd/Ctrl+K to open search\n * useKeyboardShortcut('k', openSearch, { modifiers: { meta: true, ctrl: true } });\n *\n * // Forward slash to focus search (ignoring when typing)\n * useKeyboardShortcut('/', focusSearch);\n *\n * // Escape to close modal\n * useKeyboardShortcut('Escape', closeModal);\n * ```\n */\nexport function useKeyboardShortcut(\n key: string,\n callback: (event: KeyboardEvent) => void,\n options: KeyboardShortcutOptions = {}\n): void {\n const {\n enabled = true,\n modifiers = {},\n preventDefault = true,\n stopPropagation = false,\n ignoreInputs = true,\n } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // Check if we should ignore input fields\n if (ignoreInputs) {\n const target = event.target as HTMLElement;\n const tagName = target.tagName.toUpperCase();\n if (\n tagName === 'INPUT' ||\n tagName === 'TEXTAREA' ||\n tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n // Check modifiers\n const { ctrl, shift, alt, meta } = modifiers;\n\n // For Cmd+K style shortcuts, allow either meta (Mac) or ctrl (Windows/Linux)\n const metaOrCtrl = meta || ctrl;\n if (metaOrCtrl) {\n if (!(event.metaKey || event.ctrlKey)) return;\n } else {\n // If no meta/ctrl modifier specified, ensure neither is pressed\n if (event.metaKey || event.ctrlKey) return;\n }\n\n if (shift && !event.shiftKey) return;\n if (alt && !event.altKey) return;\n\n // Check key match (case-insensitive for letters)\n const eventKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const targetKey = key.length === 1 ? key.toLowerCase() : key;\n if (eventKey !== targetKey) return;\n\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n callback(event);\n },\n [key, callback, modifiers, preventDefault, stopPropagation, ignoreInputs]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, enabled]);\n}\n\n/**\n * Hook for the common Cmd/Ctrl+K shortcut pattern (command palette, search, etc.)\n *\n * @param callback - Function to call when Cmd/Ctrl+K is pressed\n * @param enabled - Whether the shortcut is active (default: true)\n *\n * @example\n * ```tsx\n * useCommandK(() => setIsOpen(true));\n * ```\n */\nexport function useCommandK(callback: () => void, enabled = true): void {\n useKeyboardShortcut('k', callback, {\n enabled,\n modifiers: { meta: true, ctrl: true },\n ignoreInputs: false, // Cmd+K should work even in inputs\n });\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Hook that tracks whether a media query matches.\n * Uses the native `matchMedia` API for efficient media query tracking.\n *\n * @param query - CSS media query string (e.g., '(min-width: 768px)')\n * @returns Boolean indicating whether the media query matches\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n * const isMobile = useMediaQuery('(max-width: 767px)');\n * const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n *\n * return (\n * <div>\n * {isMobile && <MobileLayout />}\n * {isTablet && <TabletLayout />}\n * {isDesktop && <DesktopLayout />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaQuery(query: string): boolean {\n // Initialize with null to indicate SSR/initial state\n const [matches, setMatches] = useState<boolean>(() => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia(query);\n\n // Set initial value\n setMatches(mediaQuery.matches);\n\n // Handler for media query changes\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Modern browsers use addEventListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }\n\n // Fallback for older browsers\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n\n/**\n * Preset breakpoint hooks following common responsive design patterns\n */\n\n/** Returns true when viewport is smaller than 640px (mobile) */\nexport function useIsMobile(): boolean {\n return useMediaQuery('(max-width: 639px)');\n}\n\n/** Returns true when viewport is 640px-767px (large mobile / small tablet) */\nexport function useIsSmallTablet(): boolean {\n return useMediaQuery('(min-width: 640px) and (max-width: 767px)');\n}\n\n/** Returns true when viewport is 768px-1023px (tablet) */\nexport function useIsTablet(): boolean {\n return useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n}\n\n/** Returns true when viewport is 1024px or larger (desktop) */\nexport function useIsDesktop(): boolean {\n return useMediaQuery('(min-width: 1024px)');\n}\n\n/** Returns true when viewport is 1280px or larger (large desktop) */\nexport function useIsLargeDesktop(): boolean {\n return useMediaQuery('(min-width: 1280px)');\n}\n\n/** Returns true when viewport is smaller than 1024px (mobile/tablet) */\nexport function useIsMobileOrTablet(): boolean {\n return useMediaQuery('(max-width: 1023px)');\n}\n"]}
@@ -0,0 +1,269 @@
1
+ import { useEscapeKey } from './chunk-T4ME7QCT.js';
2
+ import { useAnchoredPosition } from './chunk-BOOJDRJO.js';
3
+ import { cn } from './chunk-TQONQSJO.js';
4
+ import * as React from 'react';
5
+ import { createPortal } from 'react-dom';
6
+ import { X, Diamond, ArrowRight } from 'lucide-react';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ var HIDE_GRACE_MS = 180;
10
+ function GlossaryTooltip({
11
+ children,
12
+ term,
13
+ definition,
14
+ category,
15
+ keyFact,
16
+ source,
17
+ related,
18
+ href,
19
+ maxDefinitionLength = 190,
20
+ underline = true,
21
+ className
22
+ }) {
23
+ const [open, setOpen] = React.useState(false);
24
+ const [pinned, setPinned] = React.useState(false);
25
+ const timer = React.useRef(null);
26
+ const suppressFocusShow = React.useRef(false);
27
+ const { anchorRef, floatingRef, style } = useAnchoredPosition({
28
+ open,
29
+ placement: "top",
30
+ offset: 10,
31
+ viewportPadding: 8,
32
+ allowFlip: true
33
+ });
34
+ const clearHideTimer = () => {
35
+ if (timer.current) {
36
+ clearTimeout(timer.current);
37
+ timer.current = null;
38
+ }
39
+ };
40
+ const show = React.useCallback(() => {
41
+ clearHideTimer();
42
+ setOpen(true);
43
+ }, []);
44
+ const scheduleHide = React.useCallback(() => {
45
+ if (pinned) return;
46
+ clearHideTimer();
47
+ timer.current = setTimeout(() => setOpen(false), HIDE_GRACE_MS);
48
+ }, [pinned]);
49
+ const close = React.useCallback(() => {
50
+ clearHideTimer();
51
+ setPinned(false);
52
+ setOpen(false);
53
+ if (floatingRef.current?.contains(document.activeElement)) {
54
+ suppressFocusShow.current = true;
55
+ anchorRef.current?.querySelector("a,button")?.focus();
56
+ }
57
+ }, [anchorRef, floatingRef]);
58
+ React.useEffect(() => () => clearHideTimer(), []);
59
+ React.useEffect(() => {
60
+ if (pinned) floatingRef.current?.focus();
61
+ }, [pinned, floatingRef]);
62
+ useEscapeKey(close, open);
63
+ React.useEffect(() => {
64
+ if (!open || !pinned) return;
65
+ const onPointer = (e) => {
66
+ const t = e.target;
67
+ if (anchorRef.current?.contains(t) || floatingRef.current?.contains(t))
68
+ return;
69
+ close();
70
+ };
71
+ document.addEventListener("mousedown", onPointer);
72
+ document.addEventListener("touchstart", onPointer);
73
+ return () => {
74
+ document.removeEventListener("mousedown", onPointer);
75
+ document.removeEventListener("touchstart", onPointer);
76
+ };
77
+ }, [open, pinned, close, anchorRef, floatingRef]);
78
+ const togglePin = React.useCallback(() => {
79
+ if (pinned) close();
80
+ else {
81
+ setPinned(true);
82
+ show();
83
+ }
84
+ }, [pinned, close, show]);
85
+ const onTriggerClick = (e) => {
86
+ const coarse = typeof window !== "undefined" && window.matchMedia("(hover: none)").matches;
87
+ if (href) {
88
+ if (!coarse) return;
89
+ e.preventDefault();
90
+ }
91
+ togglePin();
92
+ };
93
+ const onTriggerKeyDown = (e) => {
94
+ if (href) return;
95
+ if (e.key === "Enter" || e.key === " ") {
96
+ e.preventDefault();
97
+ togglePin();
98
+ }
99
+ };
100
+ const onDialogKeyDown = (e) => {
101
+ if (e.target !== e.currentTarget) return;
102
+ if (e.key === "Enter" || e.key === " ") {
103
+ e.preventDefault();
104
+ close();
105
+ }
106
+ };
107
+ const onTriggerFocus = () => {
108
+ if (suppressFocusShow.current) {
109
+ suppressFocusShow.current = false;
110
+ return;
111
+ }
112
+ show();
113
+ };
114
+ const onBlurCapture = (e) => {
115
+ const next = e.relatedTarget;
116
+ if (next && (anchorRef.current?.contains(next) || floatingRef.current?.contains(next)))
117
+ return;
118
+ scheduleHide();
119
+ };
120
+ const limit = Math.max(1, maxDefinitionLength);
121
+ const short = definition.length > limit ? `${definition.slice(0, limit - 1).trimEnd()}\u2026` : definition;
122
+ const triggerClasses = cn(
123
+ "cursor-help",
124
+ underline && "decoration-primary-400/70 underline decoration-dashed underline-offset-4",
125
+ className
126
+ );
127
+ const triggerProps = {
128
+ onClick: onTriggerClick,
129
+ className: cn(
130
+ triggerClasses,
131
+ href && "text-primary-800 dark:text-primary-400 font-medium"
132
+ )
133
+ };
134
+ return /* @__PURE__ */ jsxs(
135
+ "span",
136
+ {
137
+ ref: anchorRef,
138
+ className: "relative inline",
139
+ onMouseEnter: show,
140
+ onMouseLeave: scheduleHide,
141
+ onFocus: onTriggerFocus,
142
+ onBlur: onBlurCapture,
143
+ children: [
144
+ href ? /* @__PURE__ */ jsx(
145
+ "a",
146
+ {
147
+ href,
148
+ "aria-expanded": open,
149
+ "aria-haspopup": "dialog",
150
+ ...triggerProps,
151
+ children
152
+ }
153
+ ) : /* @__PURE__ */ jsx(
154
+ "button",
155
+ {
156
+ type: "button",
157
+ "aria-expanded": open,
158
+ "aria-haspopup": "dialog",
159
+ "aria-label": `${term} \u2014 show definition`,
160
+ onKeyDown: onTriggerKeyDown,
161
+ ...triggerProps,
162
+ className: cn(
163
+ "inline appearance-none border-0 bg-transparent p-0 text-inherit [font:inherit]",
164
+ triggerProps.className
165
+ ),
166
+ children
167
+ }
168
+ ),
169
+ open && createPortal(
170
+ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- keyboard dismissal on a focused non-modal dialog container is the WAI-ARIA pattern; links inside keep their own semantics */
171
+ /* @__PURE__ */ jsx(
172
+ "span",
173
+ {
174
+ ref: floatingRef,
175
+ role: "dialog",
176
+ "aria-label": term,
177
+ tabIndex: -1,
178
+ onMouseEnter: show,
179
+ onMouseLeave: scheduleHide,
180
+ onBlurCapture,
181
+ onKeyDown: onDialogKeyDown,
182
+ className: "focus-visible:ring-ring z-50 block w-80 rounded-lg outline-none focus-visible:ring-2",
183
+ style,
184
+ children: /* @__PURE__ */ jsxs("span", { className: "border-border bg-card block overflow-hidden rounded-lg border text-start font-normal tracking-normal normal-case shadow-xl ring-1 ring-black/5 dark:ring-white/10", children: [
185
+ /* @__PURE__ */ jsx("span", { className: "from-primary-700 to-primary-600 block bg-gradient-to-br px-3.5 py-2.5", children: /* @__PURE__ */ jsxs("span", { className: "flex items-start justify-between gap-2", children: [
186
+ /* @__PURE__ */ jsxs("span", { className: "block min-w-0", children: [
187
+ category && /* @__PURE__ */ jsx("span", { className: "mb-1 inline-flex rounded-full bg-white/15 px-2 py-0.5 text-[10px] font-bold tracking-wide text-white uppercase", children: category }),
188
+ /* @__PURE__ */ jsx("span", { className: "block text-sm leading-snug font-semibold text-white", children: term })
189
+ ] }),
190
+ pinned && /* @__PURE__ */ jsx(
191
+ "button",
192
+ {
193
+ type: "button",
194
+ onClick: close,
195
+ "aria-label": "Close definition",
196
+ className: "shrink-0 rounded p-0.5 text-white/70 hover:bg-white/10 hover:text-white",
197
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
198
+ }
199
+ )
200
+ ] }) }),
201
+ /* @__PURE__ */ jsxs("span", { className: "block px-3.5 py-2.5", children: [
202
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground block text-[13px] leading-relaxed", children: short }),
203
+ keyFact && /* @__PURE__ */ jsxs("span", { className: "mt-2 flex items-start gap-1.5", children: [
204
+ /* @__PURE__ */ jsx(
205
+ Diamond,
206
+ {
207
+ "aria-hidden": "true",
208
+ className: "text-primary-500 mt-0.5 h-3 w-3 shrink-0"
209
+ }
210
+ ),
211
+ /* @__PURE__ */ jsx("span", { className: "text-foreground min-w-0 text-xs leading-relaxed font-medium", children: keyFact })
212
+ ] })
213
+ ] }),
214
+ (source || related && related.length > 0 || href) && /* @__PURE__ */ jsxs("span", { className: "border-border bg-muted/50 block border-t px-3.5 py-2", children: [
215
+ related && related.length > 0 && /* @__PURE__ */ jsx("span", { className: "mb-1.5 flex flex-wrap gap-1", children: related.map(
216
+ (r) => r.href ? /* @__PURE__ */ jsx(
217
+ "a",
218
+ {
219
+ href: r.href,
220
+ className: "border-border bg-card text-foreground hover:border-primary-400 hover:text-primary-800 dark:hover:text-primary-400 rounded-full border px-2 py-0.5 text-[11px] font-medium",
221
+ children: r.term
222
+ },
223
+ r.term
224
+ ) : /* @__PURE__ */ jsx(
225
+ "span",
226
+ {
227
+ className: "border-border bg-card text-muted-foreground rounded-full border px-2 py-0.5 text-[11px] font-medium",
228
+ children: r.term
229
+ },
230
+ r.term
231
+ )
232
+ ) }),
233
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-2", children: [
234
+ source ? /* @__PURE__ */ jsx(
235
+ "a",
236
+ {
237
+ href: source.url,
238
+ target: "_blank",
239
+ rel: "noopener noreferrer",
240
+ className: "text-primary-800 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 min-w-0 truncate text-[11px] font-semibold",
241
+ children: source.label
242
+ }
243
+ ) : /* @__PURE__ */ jsx("span", {}),
244
+ href && /* @__PURE__ */ jsxs(
245
+ "a",
246
+ {
247
+ href,
248
+ className: "text-muted-foreground hover:text-foreground inline-flex shrink-0 items-center gap-0.5 text-[11px] font-medium",
249
+ children: [
250
+ "Full definition",
251
+ /* @__PURE__ */ jsx(ArrowRight, { "aria-hidden": "true", className: "h-3 w-3" })
252
+ ]
253
+ }
254
+ )
255
+ ] })
256
+ ] })
257
+ ] })
258
+ }
259
+ ),
260
+ document.body
261
+ )
262
+ ]
263
+ }
264
+ );
265
+ }
266
+
267
+ export { GlossaryTooltip };
268
+ //# sourceMappingURL=chunk-XWYKLEKG.js.map
269
+ //# sourceMappingURL=chunk-XWYKLEKG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/GlossaryTooltip/GlossaryTooltip.tsx"],"names":[],"mappings":"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","file":"chunk-XWYKLEKG.js","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { ArrowRight, Diamond, X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useAnchoredPosition } from '../../hooks/useAnchoredPosition';\nimport { useEscapeKey } from '../../hooks/useEscapeKey';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Authoritative source link shown in the card footer. */\nexport interface GlossarySource {\n label: string;\n url: string;\n}\n\n/** Sibling term shown as a lateral \"hop\" chip. */\nexport interface GlossaryRelatedTerm {\n term: string;\n href?: string;\n}\n\nexport interface GlossaryTooltipProps {\n /** Visible, in-flow trigger text (may be a plural or abbreviation match). */\n children: React.ReactNode;\n /** Canonical term name, shown as the card heading. */\n term: string;\n definition: string;\n /** Human-readable category label, shown as a badge in the header. */\n category?: string;\n /** Optional single key fact, shown with a diamond marker. */\n keyFact?: string;\n /** Optional authoritative source link, shown in the footer. */\n source?: GlossarySource;\n /** Optional sibling terms, shown as chips. */\n related?: GlossaryRelatedTerm[];\n /**\n * Full glossary page for the term. When set, the trigger is a real link —\n * desktop clicks navigate while hover previews; touch taps pin the card.\n */\n href?: string;\n /** Truncation limit for the in-card definition (default 190 chars). */\n maxDefinitionLength?: number;\n /** Dashed underline marking the term as hoverable (default true). */\n underline?: boolean;\n className?: string;\n}\n\nconst HIDE_GRACE_MS = 180;\n\n// =============================================================================\n// GlossaryTooltip\n// =============================================================================\n\n/**\n * A rich \"what does this term mean?\" hover card: category badge, canonical\n * term, truncated definition, optional key fact, source link, and related\n * term chips. The provenance sibling is `SourceTip` (\"what backs this\n * claim?\") — the two share the same interaction grammar.\n *\n * Trigger and card are phrasing content (`span`/`a`/`button` only), so it is\n * valid inside `<p>` prose. The card portals to `body` with fixed\n * positioning, flipping and clamping to stay in the viewport. On touch, a\n * tap pins the card instead of navigating; Escape or an outside tap closes.\n *\n * @example\n * ```tsx\n * <GlossaryTooltip\n * term=\"OSHA recordable\"\n * definition=\"A work-related injury or illness that meets OSHA reporting criteria…\"\n * category=\"Compliance\"\n * href=\"/glossary/osha-recordable\"\n * >\n * OSHA recordables\n * </GlossaryTooltip>\n * ```\n */\nexport function GlossaryTooltip({\n children,\n term,\n definition,\n category,\n keyFact,\n source,\n related,\n href,\n maxDefinitionLength = 190,\n underline = true,\n className,\n}: GlossaryTooltipProps) {\n const [open, setOpen] = React.useState(false);\n const [pinned, setPinned] = React.useState(false);\n const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n // True while close() restores focus to the trigger, so the trigger's\n // focus-show doesn't immediately reopen a card the user just dismissed.\n const suppressFocusShow = React.useRef(false);\n\n const { anchorRef, floatingRef, style } = useAnchoredPosition<\n HTMLSpanElement,\n HTMLSpanElement\n >({\n open,\n placement: 'top',\n offset: 10,\n viewportPadding: 8,\n allowFlip: true,\n });\n\n const clearHideTimer = () => {\n if (timer.current) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n };\n\n const show = React.useCallback(() => {\n clearHideTimer();\n setOpen(true);\n }, []);\n\n const scheduleHide = React.useCallback(() => {\n if (pinned) return;\n clearHideTimer();\n timer.current = setTimeout(() => setOpen(false), HIDE_GRACE_MS);\n }, [pinned]);\n\n const close = React.useCallback(() => {\n clearHideTimer();\n setPinned(false);\n setOpen(false);\n // Return focus to the trigger if it was inside the card.\n if (floatingRef.current?.contains(document.activeElement)) {\n suppressFocusShow.current = true;\n (anchorRef.current?.querySelector('a,button') as HTMLElement)?.focus();\n }\n }, [anchorRef, floatingRef]);\n\n React.useEffect(() => () => clearHideTimer(), []);\n\n // A pinned card is a non-modal dialog: move focus into it so its links\n // and close button are reachable by keyboard.\n React.useEffect(() => {\n if (pinned) floatingRef.current?.focus();\n }, [pinned, floatingRef]);\n\n useEscapeKey(close, open);\n\n // Pinned cards close on an outside tap; hover cards close on their own.\n React.useEffect(() => {\n if (!open || !pinned) return;\n const onPointer = (e: Event) => {\n const t = e.target as Node;\n if (anchorRef.current?.contains(t) || floatingRef.current?.contains(t))\n return;\n close();\n };\n document.addEventListener('mousedown', onPointer);\n document.addEventListener('touchstart', onPointer);\n return () => {\n document.removeEventListener('mousedown', onPointer);\n document.removeEventListener('touchstart', onPointer);\n };\n }, [open, pinned, close, anchorRef, floatingRef]);\n\n const togglePin = React.useCallback(() => {\n if (pinned) close();\n else {\n setPinned(true);\n show();\n }\n }, [pinned, close, show]);\n\n // Linked terms: desktop clicks navigate, coarse pointers pin instead.\n // Unlinked (button) terms: any click toggles the pin — hover previews,\n // click holds the card open.\n const onTriggerClick = (e: React.MouseEvent) => {\n const coarse =\n typeof window !== 'undefined' &&\n window.matchMedia('(hover: none)').matches;\n if (href) {\n if (!coarse) return;\n e.preventDefault();\n }\n togglePin();\n };\n\n const onTriggerKeyDown = (e: React.KeyboardEvent) => {\n if (href) return; // links keep native Enter navigation\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n togglePin();\n }\n };\n\n // Focus lives on the dialog container once pinned, so Enter/Space there\n // (not on a link or the close button inside) must also unpin.\n const onDialogKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {\n if (e.target !== e.currentTarget) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n close();\n }\n };\n\n const onTriggerFocus = () => {\n if (suppressFocusShow.current) {\n suppressFocusShow.current = false;\n return;\n }\n show();\n };\n\n const onBlurCapture = (e: React.FocusEvent<HTMLSpanElement>) => {\n const next = e.relatedTarget as Node | null;\n if (\n next &&\n (anchorRef.current?.contains(next) || floatingRef.current?.contains(next))\n )\n return;\n scheduleHide();\n };\n\n const limit = Math.max(1, maxDefinitionLength);\n const short =\n definition.length > limit\n ? `${definition.slice(0, limit - 1).trimEnd()}…`\n : definition;\n\n const triggerClasses = cn(\n 'cursor-help',\n underline &&\n 'decoration-primary-400/70 underline decoration-dashed underline-offset-4',\n className\n );\n\n const triggerProps = {\n onClick: onTriggerClick,\n className: cn(\n triggerClasses,\n href && 'text-primary-800 dark:text-primary-400 font-medium'\n ),\n };\n\n return (\n <span\n ref={anchorRef}\n className=\"relative inline\"\n onMouseEnter={show}\n onMouseLeave={scheduleHide}\n onFocus={onTriggerFocus}\n onBlur={onBlurCapture}\n >\n {href ? (\n <a\n href={href}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n {...triggerProps}\n >\n {children}\n </a>\n ) : (\n <button\n type=\"button\"\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-label={`${term} — show definition`}\n onKeyDown={onTriggerKeyDown}\n {...triggerProps}\n className={cn(\n 'inline appearance-none border-0 bg-transparent p-0 text-inherit [font:inherit]',\n triggerProps.className\n )}\n >\n {children}\n </button>\n )}\n {open &&\n createPortal(\n /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- keyboard dismissal on a focused non-modal dialog container is the WAI-ARIA pattern; links inside keep their own semantics */\n <span\n ref={floatingRef}\n role=\"dialog\"\n aria-label={term}\n tabIndex={-1}\n onMouseEnter={show}\n onMouseLeave={scheduleHide}\n onBlurCapture={onBlurCapture}\n onKeyDown={onDialogKeyDown}\n className=\"focus-visible:ring-ring z-50 block w-80 rounded-lg outline-none focus-visible:ring-2\"\n style={style}\n >\n <span className=\"border-border bg-card block overflow-hidden rounded-lg border text-start font-normal tracking-normal normal-case shadow-xl ring-1 ring-black/5 dark:ring-white/10\">\n <span className=\"from-primary-700 to-primary-600 block bg-gradient-to-br px-3.5 py-2.5\">\n <span className=\"flex items-start justify-between gap-2\">\n <span className=\"block min-w-0\">\n {category && (\n <span className=\"mb-1 inline-flex rounded-full bg-white/15 px-2 py-0.5 text-[10px] font-bold tracking-wide text-white uppercase\">\n {category}\n </span>\n )}\n <span className=\"block text-sm leading-snug font-semibold text-white\">\n {term}\n </span>\n </span>\n {pinned && (\n <button\n type=\"button\"\n onClick={close}\n aria-label=\"Close definition\"\n className=\"shrink-0 rounded p-0.5 text-white/70 hover:bg-white/10 hover:text-white\"\n >\n <X aria-hidden=\"true\" className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </span>\n </span>\n\n <span className=\"block px-3.5 py-2.5\">\n <span className=\"text-muted-foreground block text-[13px] leading-relaxed\">\n {short}\n </span>\n {keyFact && (\n <span className=\"mt-2 flex items-start gap-1.5\">\n <Diamond\n aria-hidden=\"true\"\n className=\"text-primary-500 mt-0.5 h-3 w-3 shrink-0\"\n />\n <span className=\"text-foreground min-w-0 text-xs leading-relaxed font-medium\">\n {keyFact}\n </span>\n </span>\n )}\n </span>\n\n {(source || (related && related.length > 0) || href) && (\n <span className=\"border-border bg-muted/50 block border-t px-3.5 py-2\">\n {related && related.length > 0 && (\n <span className=\"mb-1.5 flex flex-wrap gap-1\">\n {related.map((r) =>\n r.href ? (\n <a\n key={r.term}\n href={r.href}\n className=\"border-border bg-card text-foreground hover:border-primary-400 hover:text-primary-800 dark:hover:text-primary-400 rounded-full border px-2 py-0.5 text-[11px] font-medium\"\n >\n {r.term}\n </a>\n ) : (\n <span\n key={r.term}\n className=\"border-border bg-card text-muted-foreground rounded-full border px-2 py-0.5 text-[11px] font-medium\"\n >\n {r.term}\n </span>\n )\n )}\n </span>\n )}\n <span className=\"flex items-center justify-between gap-2\">\n {source ? (\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary-800 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300 min-w-0 truncate text-[11px] font-semibold\"\n >\n {source.label}\n </a>\n ) : (\n <span />\n )}\n {href && (\n <a\n href={href}\n className=\"text-muted-foreground hover:text-foreground inline-flex shrink-0 items-center gap-0.5 text-[11px] font-medium\"\n >\n Full definition\n <ArrowRight aria-hidden=\"true\" className=\"h-3 w-3\" />\n </a>\n )}\n </span>\n </span>\n )}\n </span>\n </span>,\n document.body\n )}\n </span>\n );\n}\n"]}
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ var chunk7L7OPISJ_cjs = require('../../chunk-7L7OPISJ.cjs');
4
+ require('../../chunk-FHY3K6PL.cjs');
5
+ require('../../chunk-MYFZYUPR.cjs');
6
+ require('../../chunk-S5RVSXDV.cjs');
7
+
8
+
9
+
10
+ Object.defineProperty(exports, "GlossaryTooltip", {
11
+ enumerable: true,
12
+ get: function () { return chunk7L7OPISJ_cjs.GlossaryTooltip; }
13
+ });
14
+ //# sourceMappingURL=index.cjs.map
15
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -0,0 +1,64 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /** Authoritative source link shown in the card footer. */
5
+ interface GlossarySource {
6
+ label: string;
7
+ url: string;
8
+ }
9
+ /** Sibling term shown as a lateral "hop" chip. */
10
+ interface GlossaryRelatedTerm {
11
+ term: string;
12
+ href?: string;
13
+ }
14
+ interface GlossaryTooltipProps {
15
+ /** Visible, in-flow trigger text (may be a plural or abbreviation match). */
16
+ children: React.ReactNode;
17
+ /** Canonical term name, shown as the card heading. */
18
+ term: string;
19
+ definition: string;
20
+ /** Human-readable category label, shown as a badge in the header. */
21
+ category?: string;
22
+ /** Optional single key fact, shown with a diamond marker. */
23
+ keyFact?: string;
24
+ /** Optional authoritative source link, shown in the footer. */
25
+ source?: GlossarySource;
26
+ /** Optional sibling terms, shown as chips. */
27
+ related?: GlossaryRelatedTerm[];
28
+ /**
29
+ * Full glossary page for the term. When set, the trigger is a real link —
30
+ * desktop clicks navigate while hover previews; touch taps pin the card.
31
+ */
32
+ href?: string;
33
+ /** Truncation limit for the in-card definition (default 190 chars). */
34
+ maxDefinitionLength?: number;
35
+ /** Dashed underline marking the term as hoverable (default true). */
36
+ underline?: boolean;
37
+ className?: string;
38
+ }
39
+ /**
40
+ * A rich "what does this term mean?" hover card: category badge, canonical
41
+ * term, truncated definition, optional key fact, source link, and related
42
+ * term chips. The provenance sibling is `SourceTip` ("what backs this
43
+ * claim?") — the two share the same interaction grammar.
44
+ *
45
+ * Trigger and card are phrasing content (`span`/`a`/`button` only), so it is
46
+ * valid inside `<p>` prose. The card portals to `body` with fixed
47
+ * positioning, flipping and clamping to stay in the viewport. On touch, a
48
+ * tap pins the card instead of navigating; Escape or an outside tap closes.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * <GlossaryTooltip
53
+ * term="OSHA recordable"
54
+ * definition="A work-related injury or illness that meets OSHA reporting criteria…"
55
+ * category="Compliance"
56
+ * href="/glossary/osha-recordable"
57
+ * >
58
+ * OSHA recordables
59
+ * </GlossaryTooltip>
60
+ * ```
61
+ */
62
+ declare function GlossaryTooltip({ children, term, definition, category, keyFact, source, related, href, maxDefinitionLength, underline, className, }: GlossaryTooltipProps): react_jsx_runtime.JSX.Element;
63
+
64
+ export { type GlossaryRelatedTerm, type GlossarySource, GlossaryTooltip, type GlossaryTooltipProps };
@@ -0,0 +1,64 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /** Authoritative source link shown in the card footer. */
5
+ interface GlossarySource {
6
+ label: string;
7
+ url: string;
8
+ }
9
+ /** Sibling term shown as a lateral "hop" chip. */
10
+ interface GlossaryRelatedTerm {
11
+ term: string;
12
+ href?: string;
13
+ }
14
+ interface GlossaryTooltipProps {
15
+ /** Visible, in-flow trigger text (may be a plural or abbreviation match). */
16
+ children: React.ReactNode;
17
+ /** Canonical term name, shown as the card heading. */
18
+ term: string;
19
+ definition: string;
20
+ /** Human-readable category label, shown as a badge in the header. */
21
+ category?: string;
22
+ /** Optional single key fact, shown with a diamond marker. */
23
+ keyFact?: string;
24
+ /** Optional authoritative source link, shown in the footer. */
25
+ source?: GlossarySource;
26
+ /** Optional sibling terms, shown as chips. */
27
+ related?: GlossaryRelatedTerm[];
28
+ /**
29
+ * Full glossary page for the term. When set, the trigger is a real link —
30
+ * desktop clicks navigate while hover previews; touch taps pin the card.
31
+ */
32
+ href?: string;
33
+ /** Truncation limit for the in-card definition (default 190 chars). */
34
+ maxDefinitionLength?: number;
35
+ /** Dashed underline marking the term as hoverable (default true). */
36
+ underline?: boolean;
37
+ className?: string;
38
+ }
39
+ /**
40
+ * A rich "what does this term mean?" hover card: category badge, canonical
41
+ * term, truncated definition, optional key fact, source link, and related
42
+ * term chips. The provenance sibling is `SourceTip` ("what backs this
43
+ * claim?") — the two share the same interaction grammar.
44
+ *
45
+ * Trigger and card are phrasing content (`span`/`a`/`button` only), so it is
46
+ * valid inside `<p>` prose. The card portals to `body` with fixed
47
+ * positioning, flipping and clamping to stay in the viewport. On touch, a
48
+ * tap pins the card instead of navigating; Escape or an outside tap closes.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * <GlossaryTooltip
53
+ * term="OSHA recordable"
54
+ * definition="A work-related injury or illness that meets OSHA reporting criteria…"
55
+ * category="Compliance"
56
+ * href="/glossary/osha-recordable"
57
+ * >
58
+ * OSHA recordables
59
+ * </GlossaryTooltip>
60
+ * ```
61
+ */
62
+ declare function GlossaryTooltip({ children, term, definition, category, keyFact, source, related, href, maxDefinitionLength, underline, className, }: GlossaryTooltipProps): react_jsx_runtime.JSX.Element;
63
+
64
+ export { type GlossaryRelatedTerm, type GlossarySource, GlossaryTooltip, type GlossaryTooltipProps };
@@ -0,0 +1,6 @@
1
+ export { GlossaryTooltip } from '../../chunk-XWYKLEKG.js';
2
+ import '../../chunk-T4ME7QCT.js';
3
+ import '../../chunk-BOOJDRJO.js';
4
+ import '../../chunk-TQONQSJO.js';
5
+ //# sourceMappingURL=index.js.map
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}