@neo4j-ndl/react 4.0.8 → 4.0.9

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.
@@ -47,7 +47,9 @@ const SpotlightProvider = ({ children, hasOverlay = true, isOverlayTransparent =
47
47
  // Calculate target element position and size for mask
48
48
  (0, react_2.useEffect)(() => {
49
49
  var _a;
50
- if (active !== null && ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
50
+ if (active !== null &&
51
+ targets[active] !== undefined &&
52
+ ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
51
53
  const element = targets[active].current;
52
54
  const rect = element.getBoundingClientRect();
53
55
  setTargetRect(rect);
@@ -77,7 +79,9 @@ const SpotlightProvider = ({ children, hasOverlay = true, isOverlayTransparent =
77
79
  // Get the border radius from the target element
78
80
  const getHoleRadius = (0, react_2.useCallback)(() => {
79
81
  var _a;
80
- if (active !== null && ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
82
+ if (active !== null &&
83
+ targets[active] !== undefined &&
84
+ ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
81
85
  const element = targets[active].current;
82
86
  const computedStyle = window.getComputedStyle(element);
83
87
  const borderRadius = computedStyle.borderRadius;
@@ -1 +1 @@
1
- {"version":3,"file":"SpotlightProvider.js","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { FloatingOverlay, FloatingPortal } from '@floating-ui/react';\nimport classNames from 'classnames';\nimport type React from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\ntype SpotlightProviderProps = {\n /** The app/screen content to wrap */\n children: React.ReactNode;\n /** A boolean that decides if there is a gray overlay when a spotlight is open. */\n hasOverlay?: boolean;\n /** If the overlay should be transparent */\n isOverlayTransparent?: boolean;\n};\n\ntype SpotlightContextType = {\n /** A boolean that indicates if a spotlight is open */\n isOpen: boolean;\n /** A function that can be used to close or open the spotlight */\n setIsOpen: (arg: boolean) => void;\n /** A function that can be used to check if a spotlight is active. It takes a target id as an argument */\n isActiveSpotlight: (target: string) => boolean;\n /** A function that can be used to set a spotlight as active. It takes a target id as an argument, or null to clear the active spotlight */\n setActiveSpotlight: (target: string | null) => void;\n /** A function that can be used to register a target. It takes a target id, and a ref to the target element as arguments */\n registerTarget: (id: string, ref: React.RefObject<HTMLElement>) => void;\n /** A function that can be used to unregister a target. It takes a target id as an argument */\n unregisterTarget: (id: string) => void;\n /** An object that contains all registered targets. The keys are the target ids, and the values are the refs to the target elements */\n registeredTargets: Record<string, React.RefObject<HTMLElement>>;\n /** A boolean that decides if there is a gray overlay when a spotlight is open */\n hasOverlay?: boolean;\n};\n\nconst SpotlightContext = createContext<SpotlightContextType | null>(null);\n\nexport const SpotlightProvider = ({\n children,\n hasOverlay = true,\n isOverlayTransparent = false,\n}: SpotlightProviderProps) => {\n const [active, setActive] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [targets, setTargets] = useState<\n Record<string, React.RefObject<HTMLElement>>\n >({});\n const [targetRect, setTargetRect] = useState<DOMRect | null>(null);\n\n const registerTarget = useCallback(\n (id: string, ref: React.RefObject<HTMLElement>) => {\n setTargets((prev) => ({ ...prev, [id]: ref }));\n },\n [],\n );\n\n const unregisterTarget = useCallback((id: string) => {\n setTargets((prev) => {\n const newTargets = { ...prev };\n delete newTargets[id];\n return newTargets;\n });\n }, []);\n\n // Calculate target element position and size for mask\n useEffect(() => {\n if (active !== null && targets[active]?.current !== null) {\n const element = targets[active].current;\n const rect = element.getBoundingClientRect();\n setTargetRect(rect);\n } else {\n setTargetRect(null);\n }\n }, [active, targets]);\n\n const contextValue: SpotlightContextType = {\n hasOverlay,\n isActiveSpotlight: useCallback(\n (target: string) => target === active,\n [active],\n ),\n isOpen,\n registerTarget,\n registeredTargets: targets,\n setActiveSpotlight: useCallback(\n (target: string | null) => setActive(target),\n [],\n ),\n setIsOpen: useCallback((open: boolean) => setIsOpen(open), []),\n unregisterTarget,\n };\n\n const overlayClasses = classNames('ndl-spotlight-overlay', {\n 'ndl-spotlight-overlay-opaque': !isOverlayTransparent,\n 'ndl-spotlight-overlay-open': isOpen && active !== null,\n });\n\n const overlayRootRef = useRef<HTMLElement | null>(null);\n\n if (active !== null) {\n overlayRootRef.current = document.getElementById(active);\n }\n\n // Get the border radius from the target element\n const getHoleRadius = useCallback(() => {\n if (active !== null && targets[active]?.current !== null) {\n const element = targets[active].current;\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius;\n\n // Parse the border radius value (handle px, rem, em, etc.)\n const radiusValue = parseFloat(borderRadius);\n return isNaN(radiusValue) ? 0 : radiusValue;\n }\n return 0;\n }, [active, targets]);\n\n const getMaskStyle = () => {\n if (!targetRect || isOverlayTransparent) return {};\n\n const { left, top, width, height } = targetRect;\n\n const x = Math.max(0, left);\n const y = Math.max(0, top);\n const w = Math.max(0, width);\n const h = Math.max(0, height);\n const r = Math.max(0, Math.min(getHoleRadius(), Math.min(w, h) / 2));\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // SVG mask: white = visible overlay, black = hole (transparent)\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${vw}\" height=\"${vh}\" viewBox=\"0 0 ${vw} ${vh}\">\n <defs>\n <mask id=\"spotlightMask\" maskUnits=\"userSpaceOnUse\">\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"white\"/>\n <rect x=\"${x}\" y=\"${y}\" width=\"${w}\" height=\"${h}\" rx=\"${r}\" ry=\"${r}\" fill=\"black\"/>\n </mask>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"black\" mask=\"url(#spotlightMask)\"/>\n </svg>`.trim();\n\n const encoded = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;\n\n return {\n WebkitMaskImage: `url(\"${encoded}\")`,\n WebkitMaskRepeat: 'no-repeat',\n WebkitMaskSize: `${vw}px ${vh}px`,\n maskImage: `url(\"${encoded}\")`,\n maskRepeat: 'no-repeat',\n maskSize: `${vw}px ${vh}px`,\n } as React.CSSProperties;\n };\n\n /** useEffect to update the target rect, so that the mask tracks the target element */\n useEffect(() => {\n if (active == null) return;\n\n const el = targets[active]?.current;\n if (el === null || el === undefined) return;\n\n const update = () => setTargetRect(el.getBoundingClientRect());\n update();\n\n const ro = new ResizeObserver(update);\n ro.observe(el);\n\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n\n return () => {\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [active, targets]);\n\n return (\n <SpotlightContext.Provider value={contextValue}>\n {hasOverlay && active !== null && isOpen && (\n <FloatingPortal>\n <FloatingOverlay\n lockScroll={true}\n className={overlayClasses}\n style={getMaskStyle()}\n data-testid=\"ndl-spotlight-overlay\"\n />\n </FloatingPortal>\n )}\n {children}\n </SpotlightContext.Provider>\n );\n};\n\nexport const useSpotlightContext = (): SpotlightContextType => {\n const context = useContext(SpotlightContext);\n\n if (context === null) {\n throw new Error(\n 'Spotlight components must be wrapped in <SpotlightProvider />',\n );\n }\n\n return context;\n};\n"]}
1
+ {"version":3,"file":"SpotlightProvider.js","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":";;;;;;;AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,8CAAqE;AACrE,4DAAoC;AAEpC,iCAOe;AA8Bf,MAAM,gBAAgB,GAAG,IAAA,qBAAa,EAA8B,IAAI,CAAC,CAAC;AAEnE,MAAM,iBAAiB,GAAG,CAAC,EAChC,QAAQ,EACR,UAAU,GAAG,IAAI,EACjB,oBAAoB,GAAG,KAAK,GACL,EAAE,EAAE;IAC3B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAU,KAAK,CAAC,CAAC;IACrD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAEpC,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,IAAA,gBAAQ,EAAiB,IAAI,CAAC,CAAC;IAEnE,MAAM,cAAc,GAAG,IAAA,mBAAW,EAChC,CAAC,EAAU,EAAE,GAAiC,EAAE,EAAE;QAChD,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iCAAM,IAAI,KAAE,CAAC,EAAE,CAAC,EAAE,GAAG,IAAG,CAAC,CAAC;IACjD,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,gBAAgB,GAAG,IAAA,mBAAW,EAAC,CAAC,EAAU,EAAE,EAAE;QAClD,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE;YAClB,MAAM,UAAU,qBAAQ,IAAI,CAAE,CAAC;YAC/B,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;YACtB,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,sDAAsD;IACtD,IAAA,iBAAS,EAAC,GAAG,EAAE;;QACb,IACE,MAAM,KAAK,IAAI;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS;YAC7B,CAAA,MAAA,OAAO,CAAC,MAAM,CAAC,0CAAE,OAAO,MAAK,IAAI,EACjC,CAAC;YACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACxC,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;YAC7C,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,MAAM,YAAY,GAAyB;QACzC,UAAU;QACV,iBAAiB,EAAE,IAAA,mBAAW,EAC5B,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,KAAK,MAAM,EACrC,CAAC,MAAM,CAAC,CACT;QACD,MAAM;QACN,cAAc;QACd,iBAAiB,EAAE,OAAO;QAC1B,kBAAkB,EAAE,IAAA,mBAAW,EAC7B,CAAC,MAAqB,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAC5C,EAAE,CACH;QACD,SAAS,EAAE,IAAA,mBAAW,EAAC,CAAC,IAAa,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QAC9D,gBAAgB;KACjB,CAAC;IAEF,MAAM,cAAc,GAAG,IAAA,oBAAU,EAAC,uBAAuB,EAAE;QACzD,8BAA8B,EAAE,CAAC,oBAAoB;QACrD,4BAA4B,EAAE,MAAM,IAAI,MAAM,KAAK,IAAI;KACxD,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAA,cAAM,EAAqB,IAAI,CAAC,CAAC;IAExD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC;IAED,gDAAgD;IAChD,MAAM,aAAa,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;;QACrC,IACE,MAAM,KAAK,IAAI;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS;YAC7B,CAAA,MAAA,OAAO,CAAC,MAAM,CAAC,0CAAE,OAAO,MAAK,IAAI,EACjC,CAAC;YACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACxC,MAAM,aAAa,GAAG,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;YACvD,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;YAEhD,2DAA2D;YAC3D,MAAM,WAAW,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;YAC7C,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAC9C,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,IAAI,CAAC,UAAU,IAAI,oBAAoB;YAAE,OAAO,EAAE,CAAC;QAEnD,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;QAEhD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAErE,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;QAC7B,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;QAE9B,gEAAgE;QAChE,MAAM,GAAG,GAAG;uDACuC,EAAE,aAAa,EAAE,kBAAkB,EAAE,IAAI,EAAE;;;uCAG3D,EAAE,aAAa,EAAE;uBACjC,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC;;;mCAG7C,EAAE,aAAa,EAAE;aACvC,CAAC,IAAI,EAAE,CAAC;QAEjB,MAAM,OAAO,GAAG,2BAA2B,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QAErE,OAAO;YACL,eAAe,EAAE,QAAQ,OAAO,IAAI;YACpC,gBAAgB,EAAE,WAAW;YAC7B,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI;YACjC,SAAS,EAAE,QAAQ,OAAO,IAAI;YAC9B,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI;SACL,CAAC;IAC3B,CAAC,CAAC;IAEF,sFAAsF;IACtF,IAAA,iBAAS,EAAC,GAAG,EAAE;;QACb,IAAI,MAAM,IAAI,IAAI;YAAE,OAAO;QAE3B,MAAM,EAAE,GAAG,MAAA,OAAO,CAAC,MAAM,CAAC,0CAAE,OAAO,CAAC;QACpC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO;QAE5C,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,CAAC;QAC/D,MAAM,EAAE,CAAC;QAET,MAAM,EAAE,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACtC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEf,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE1C,OAAO,GAAG,EAAE;YACV,EAAE,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACnD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,OAAO,CACL,wBAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,YAAY,aAC3C,UAAU,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,CAC1C,uBAAC,sBAAc,cACb,uBAAC,uBAAe,IACd,UAAU,EAAE,IAAI,EAChB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,YAAY,EAAE,iBACT,uBAAuB,GACnC,GACa,CAClB,EACA,QAAQ,IACiB,CAC7B,CAAC;AACJ,CAAC,CAAC;AApKW,QAAA,iBAAiB,qBAoK5B;AAEK,MAAM,mBAAmB,GAAG,GAAyB,EAAE;IAC5D,MAAM,OAAO,GAAG,IAAA,kBAAU,EAAC,gBAAgB,CAAC,CAAC;IAE7C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAVW,QAAA,mBAAmB,uBAU9B","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { FloatingOverlay, FloatingPortal } from '@floating-ui/react';\nimport classNames from 'classnames';\nimport type React from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\ntype SpotlightProviderProps = {\n /** The app/screen content to wrap */\n children: React.ReactNode;\n /** A boolean that decides if there is a gray overlay when a spotlight is open. */\n hasOverlay?: boolean;\n /** If the overlay should be transparent */\n isOverlayTransparent?: boolean;\n};\n\ntype SpotlightContextType = {\n /** A boolean that indicates if a spotlight is open */\n isOpen: boolean;\n /** A function that can be used to close or open the spotlight */\n setIsOpen: (arg: boolean) => void;\n /** A function that can be used to check if a spotlight is active. It takes a target id as an argument */\n isActiveSpotlight: (target: string) => boolean;\n /** A function that can be used to set a spotlight as active. It takes a target id as an argument, or null to clear the active spotlight */\n setActiveSpotlight: (target: string | null) => void;\n /** A function that can be used to register a target. It takes a target id, and a ref to the target element as arguments */\n registerTarget: (id: string, ref: React.RefObject<HTMLElement>) => void;\n /** A function that can be used to unregister a target. It takes a target id as an argument */\n unregisterTarget: (id: string) => void;\n /** An object that contains all registered targets. The keys are the target ids, and the values are the refs to the target elements */\n registeredTargets: Record<string, React.RefObject<HTMLElement>>;\n /** A boolean that decides if there is a gray overlay when a spotlight is open */\n hasOverlay?: boolean;\n};\n\nconst SpotlightContext = createContext<SpotlightContextType | null>(null);\n\nexport const SpotlightProvider = ({\n children,\n hasOverlay = true,\n isOverlayTransparent = false,\n}: SpotlightProviderProps) => {\n const [active, setActive] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [targets, setTargets] = useState<\n Record<string, React.RefObject<HTMLElement>>\n >({});\n const [targetRect, setTargetRect] = useState<DOMRect | null>(null);\n\n const registerTarget = useCallback(\n (id: string, ref: React.RefObject<HTMLElement>) => {\n setTargets((prev) => ({ ...prev, [id]: ref }));\n },\n [],\n );\n\n const unregisterTarget = useCallback((id: string) => {\n setTargets((prev) => {\n const newTargets = { ...prev };\n delete newTargets[id];\n return newTargets;\n });\n }, []);\n\n // Calculate target element position and size for mask\n useEffect(() => {\n if (\n active !== null &&\n targets[active] !== undefined &&\n targets[active]?.current !== null\n ) {\n const element = targets[active].current;\n const rect = element.getBoundingClientRect();\n setTargetRect(rect);\n } else {\n setTargetRect(null);\n }\n }, [active, targets]);\n\n const contextValue: SpotlightContextType = {\n hasOverlay,\n isActiveSpotlight: useCallback(\n (target: string) => target === active,\n [active],\n ),\n isOpen,\n registerTarget,\n registeredTargets: targets,\n setActiveSpotlight: useCallback(\n (target: string | null) => setActive(target),\n [],\n ),\n setIsOpen: useCallback((open: boolean) => setIsOpen(open), []),\n unregisterTarget,\n };\n\n const overlayClasses = classNames('ndl-spotlight-overlay', {\n 'ndl-spotlight-overlay-opaque': !isOverlayTransparent,\n 'ndl-spotlight-overlay-open': isOpen && active !== null,\n });\n\n const overlayRootRef = useRef<HTMLElement | null>(null);\n\n if (active !== null) {\n overlayRootRef.current = document.getElementById(active);\n }\n\n // Get the border radius from the target element\n const getHoleRadius = useCallback(() => {\n if (\n active !== null &&\n targets[active] !== undefined &&\n targets[active]?.current !== null\n ) {\n const element = targets[active].current;\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius;\n\n // Parse the border radius value (handle px, rem, em, etc.)\n const radiusValue = parseFloat(borderRadius);\n return isNaN(radiusValue) ? 0 : radiusValue;\n }\n return 0;\n }, [active, targets]);\n\n const getMaskStyle = () => {\n if (!targetRect || isOverlayTransparent) return {};\n\n const { left, top, width, height } = targetRect;\n\n const x = Math.max(0, left);\n const y = Math.max(0, top);\n const w = Math.max(0, width);\n const h = Math.max(0, height);\n const r = Math.max(0, Math.min(getHoleRadius(), Math.min(w, h) / 2));\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // SVG mask: white = visible overlay, black = hole (transparent)\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${vw}\" height=\"${vh}\" viewBox=\"0 0 ${vw} ${vh}\">\n <defs>\n <mask id=\"spotlightMask\" maskUnits=\"userSpaceOnUse\">\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"white\"/>\n <rect x=\"${x}\" y=\"${y}\" width=\"${w}\" height=\"${h}\" rx=\"${r}\" ry=\"${r}\" fill=\"black\"/>\n </mask>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"black\" mask=\"url(#spotlightMask)\"/>\n </svg>`.trim();\n\n const encoded = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;\n\n return {\n WebkitMaskImage: `url(\"${encoded}\")`,\n WebkitMaskRepeat: 'no-repeat',\n WebkitMaskSize: `${vw}px ${vh}px`,\n maskImage: `url(\"${encoded}\")`,\n maskRepeat: 'no-repeat',\n maskSize: `${vw}px ${vh}px`,\n } as React.CSSProperties;\n };\n\n /** useEffect to update the target rect, so that the mask tracks the target element */\n useEffect(() => {\n if (active == null) return;\n\n const el = targets[active]?.current;\n if (el === null || el === undefined) return;\n\n const update = () => setTargetRect(el.getBoundingClientRect());\n update();\n\n const ro = new ResizeObserver(update);\n ro.observe(el);\n\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n\n return () => {\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [active, targets]);\n\n return (\n <SpotlightContext.Provider value={contextValue}>\n {hasOverlay && active !== null && isOpen && (\n <FloatingPortal>\n <FloatingOverlay\n lockScroll={true}\n className={overlayClasses}\n style={getMaskStyle()}\n data-testid=\"ndl-spotlight-overlay\"\n />\n </FloatingPortal>\n )}\n {children}\n </SpotlightContext.Provider>\n );\n};\n\nexport const useSpotlightContext = (): SpotlightContextType => {\n const context = useContext(SpotlightContext);\n\n if (context === null) {\n throw new Error(\n 'Spotlight components must be wrapped in <SpotlightProvider />',\n );\n }\n\n return context;\n};\n"]}
@@ -41,7 +41,9 @@ export const SpotlightProvider = ({ children, hasOverlay = true, isOverlayTransp
41
41
  // Calculate target element position and size for mask
42
42
  useEffect(() => {
43
43
  var _a;
44
- if (active !== null && ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
44
+ if (active !== null &&
45
+ targets[active] !== undefined &&
46
+ ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
45
47
  const element = targets[active].current;
46
48
  const rect = element.getBoundingClientRect();
47
49
  setTargetRect(rect);
@@ -71,7 +73,9 @@ export const SpotlightProvider = ({ children, hasOverlay = true, isOverlayTransp
71
73
  // Get the border radius from the target element
72
74
  const getHoleRadius = useCallback(() => {
73
75
  var _a;
74
- if (active !== null && ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
76
+ if (active !== null &&
77
+ targets[active] !== undefined &&
78
+ ((_a = targets[active]) === null || _a === void 0 ? void 0 : _a.current) !== null) {
75
79
  const element = targets[active].current;
76
80
  const computedStyle = window.getComputedStyle(element);
77
81
  const borderRadius = computedStyle.borderRadius;
@@ -1 +1 @@
1
- {"version":3,"file":"SpotlightProvider.js","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { FloatingOverlay, FloatingPortal } from '@floating-ui/react';\nimport classNames from 'classnames';\nimport type React from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\ntype SpotlightProviderProps = {\n /** The app/screen content to wrap */\n children: React.ReactNode;\n /** A boolean that decides if there is a gray overlay when a spotlight is open. */\n hasOverlay?: boolean;\n /** If the overlay should be transparent */\n isOverlayTransparent?: boolean;\n};\n\ntype SpotlightContextType = {\n /** A boolean that indicates if a spotlight is open */\n isOpen: boolean;\n /** A function that can be used to close or open the spotlight */\n setIsOpen: (arg: boolean) => void;\n /** A function that can be used to check if a spotlight is active. It takes a target id as an argument */\n isActiveSpotlight: (target: string) => boolean;\n /** A function that can be used to set a spotlight as active. It takes a target id as an argument, or null to clear the active spotlight */\n setActiveSpotlight: (target: string | null) => void;\n /** A function that can be used to register a target. It takes a target id, and a ref to the target element as arguments */\n registerTarget: (id: string, ref: React.RefObject<HTMLElement>) => void;\n /** A function that can be used to unregister a target. It takes a target id as an argument */\n unregisterTarget: (id: string) => void;\n /** An object that contains all registered targets. The keys are the target ids, and the values are the refs to the target elements */\n registeredTargets: Record<string, React.RefObject<HTMLElement>>;\n /** A boolean that decides if there is a gray overlay when a spotlight is open */\n hasOverlay?: boolean;\n};\n\nconst SpotlightContext = createContext<SpotlightContextType | null>(null);\n\nexport const SpotlightProvider = ({\n children,\n hasOverlay = true,\n isOverlayTransparent = false,\n}: SpotlightProviderProps) => {\n const [active, setActive] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [targets, setTargets] = useState<\n Record<string, React.RefObject<HTMLElement>>\n >({});\n const [targetRect, setTargetRect] = useState<DOMRect | null>(null);\n\n const registerTarget = useCallback(\n (id: string, ref: React.RefObject<HTMLElement>) => {\n setTargets((prev) => ({ ...prev, [id]: ref }));\n },\n [],\n );\n\n const unregisterTarget = useCallback((id: string) => {\n setTargets((prev) => {\n const newTargets = { ...prev };\n delete newTargets[id];\n return newTargets;\n });\n }, []);\n\n // Calculate target element position and size for mask\n useEffect(() => {\n if (active !== null && targets[active]?.current !== null) {\n const element = targets[active].current;\n const rect = element.getBoundingClientRect();\n setTargetRect(rect);\n } else {\n setTargetRect(null);\n }\n }, [active, targets]);\n\n const contextValue: SpotlightContextType = {\n hasOverlay,\n isActiveSpotlight: useCallback(\n (target: string) => target === active,\n [active],\n ),\n isOpen,\n registerTarget,\n registeredTargets: targets,\n setActiveSpotlight: useCallback(\n (target: string | null) => setActive(target),\n [],\n ),\n setIsOpen: useCallback((open: boolean) => setIsOpen(open), []),\n unregisterTarget,\n };\n\n const overlayClasses = classNames('ndl-spotlight-overlay', {\n 'ndl-spotlight-overlay-opaque': !isOverlayTransparent,\n 'ndl-spotlight-overlay-open': isOpen && active !== null,\n });\n\n const overlayRootRef = useRef<HTMLElement | null>(null);\n\n if (active !== null) {\n overlayRootRef.current = document.getElementById(active);\n }\n\n // Get the border radius from the target element\n const getHoleRadius = useCallback(() => {\n if (active !== null && targets[active]?.current !== null) {\n const element = targets[active].current;\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius;\n\n // Parse the border radius value (handle px, rem, em, etc.)\n const radiusValue = parseFloat(borderRadius);\n return isNaN(radiusValue) ? 0 : radiusValue;\n }\n return 0;\n }, [active, targets]);\n\n const getMaskStyle = () => {\n if (!targetRect || isOverlayTransparent) return {};\n\n const { left, top, width, height } = targetRect;\n\n const x = Math.max(0, left);\n const y = Math.max(0, top);\n const w = Math.max(0, width);\n const h = Math.max(0, height);\n const r = Math.max(0, Math.min(getHoleRadius(), Math.min(w, h) / 2));\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // SVG mask: white = visible overlay, black = hole (transparent)\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${vw}\" height=\"${vh}\" viewBox=\"0 0 ${vw} ${vh}\">\n <defs>\n <mask id=\"spotlightMask\" maskUnits=\"userSpaceOnUse\">\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"white\"/>\n <rect x=\"${x}\" y=\"${y}\" width=\"${w}\" height=\"${h}\" rx=\"${r}\" ry=\"${r}\" fill=\"black\"/>\n </mask>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"black\" mask=\"url(#spotlightMask)\"/>\n </svg>`.trim();\n\n const encoded = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;\n\n return {\n WebkitMaskImage: `url(\"${encoded}\")`,\n WebkitMaskRepeat: 'no-repeat',\n WebkitMaskSize: `${vw}px ${vh}px`,\n maskImage: `url(\"${encoded}\")`,\n maskRepeat: 'no-repeat',\n maskSize: `${vw}px ${vh}px`,\n } as React.CSSProperties;\n };\n\n /** useEffect to update the target rect, so that the mask tracks the target element */\n useEffect(() => {\n if (active == null) return;\n\n const el = targets[active]?.current;\n if (el === null || el === undefined) return;\n\n const update = () => setTargetRect(el.getBoundingClientRect());\n update();\n\n const ro = new ResizeObserver(update);\n ro.observe(el);\n\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n\n return () => {\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [active, targets]);\n\n return (\n <SpotlightContext.Provider value={contextValue}>\n {hasOverlay && active !== null && isOpen && (\n <FloatingPortal>\n <FloatingOverlay\n lockScroll={true}\n className={overlayClasses}\n style={getMaskStyle()}\n data-testid=\"ndl-spotlight-overlay\"\n />\n </FloatingPortal>\n )}\n {children}\n </SpotlightContext.Provider>\n );\n};\n\nexport const useSpotlightContext = (): SpotlightContextType => {\n const context = useContext(SpotlightContext);\n\n if (context === null) {\n throw new Error(\n 'Spotlight components must be wrapped in <SpotlightProvider />',\n );\n }\n\n return context;\n};\n"]}
1
+ {"version":3,"file":"SpotlightProvider.js","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport { FloatingOverlay, FloatingPortal } from '@floating-ui/react';\nimport classNames from 'classnames';\nimport type React from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\ntype SpotlightProviderProps = {\n /** The app/screen content to wrap */\n children: React.ReactNode;\n /** A boolean that decides if there is a gray overlay when a spotlight is open. */\n hasOverlay?: boolean;\n /** If the overlay should be transparent */\n isOverlayTransparent?: boolean;\n};\n\ntype SpotlightContextType = {\n /** A boolean that indicates if a spotlight is open */\n isOpen: boolean;\n /** A function that can be used to close or open the spotlight */\n setIsOpen: (arg: boolean) => void;\n /** A function that can be used to check if a spotlight is active. It takes a target id as an argument */\n isActiveSpotlight: (target: string) => boolean;\n /** A function that can be used to set a spotlight as active. It takes a target id as an argument, or null to clear the active spotlight */\n setActiveSpotlight: (target: string | null) => void;\n /** A function that can be used to register a target. It takes a target id, and a ref to the target element as arguments */\n registerTarget: (id: string, ref: React.RefObject<HTMLElement>) => void;\n /** A function that can be used to unregister a target. It takes a target id as an argument */\n unregisterTarget: (id: string) => void;\n /** An object that contains all registered targets. The keys are the target ids, and the values are the refs to the target elements */\n registeredTargets: Record<string, React.RefObject<HTMLElement>>;\n /** A boolean that decides if there is a gray overlay when a spotlight is open */\n hasOverlay?: boolean;\n};\n\nconst SpotlightContext = createContext<SpotlightContextType | null>(null);\n\nexport const SpotlightProvider = ({\n children,\n hasOverlay = true,\n isOverlayTransparent = false,\n}: SpotlightProviderProps) => {\n const [active, setActive] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [targets, setTargets] = useState<\n Record<string, React.RefObject<HTMLElement>>\n >({});\n const [targetRect, setTargetRect] = useState<DOMRect | null>(null);\n\n const registerTarget = useCallback(\n (id: string, ref: React.RefObject<HTMLElement>) => {\n setTargets((prev) => ({ ...prev, [id]: ref }));\n },\n [],\n );\n\n const unregisterTarget = useCallback((id: string) => {\n setTargets((prev) => {\n const newTargets = { ...prev };\n delete newTargets[id];\n return newTargets;\n });\n }, []);\n\n // Calculate target element position and size for mask\n useEffect(() => {\n if (\n active !== null &&\n targets[active] !== undefined &&\n targets[active]?.current !== null\n ) {\n const element = targets[active].current;\n const rect = element.getBoundingClientRect();\n setTargetRect(rect);\n } else {\n setTargetRect(null);\n }\n }, [active, targets]);\n\n const contextValue: SpotlightContextType = {\n hasOverlay,\n isActiveSpotlight: useCallback(\n (target: string) => target === active,\n [active],\n ),\n isOpen,\n registerTarget,\n registeredTargets: targets,\n setActiveSpotlight: useCallback(\n (target: string | null) => setActive(target),\n [],\n ),\n setIsOpen: useCallback((open: boolean) => setIsOpen(open), []),\n unregisterTarget,\n };\n\n const overlayClasses = classNames('ndl-spotlight-overlay', {\n 'ndl-spotlight-overlay-opaque': !isOverlayTransparent,\n 'ndl-spotlight-overlay-open': isOpen && active !== null,\n });\n\n const overlayRootRef = useRef<HTMLElement | null>(null);\n\n if (active !== null) {\n overlayRootRef.current = document.getElementById(active);\n }\n\n // Get the border radius from the target element\n const getHoleRadius = useCallback(() => {\n if (\n active !== null &&\n targets[active] !== undefined &&\n targets[active]?.current !== null\n ) {\n const element = targets[active].current;\n const computedStyle = window.getComputedStyle(element);\n const borderRadius = computedStyle.borderRadius;\n\n // Parse the border radius value (handle px, rem, em, etc.)\n const radiusValue = parseFloat(borderRadius);\n return isNaN(radiusValue) ? 0 : radiusValue;\n }\n return 0;\n }, [active, targets]);\n\n const getMaskStyle = () => {\n if (!targetRect || isOverlayTransparent) return {};\n\n const { left, top, width, height } = targetRect;\n\n const x = Math.max(0, left);\n const y = Math.max(0, top);\n const w = Math.max(0, width);\n const h = Math.max(0, height);\n const r = Math.max(0, Math.min(getHoleRadius(), Math.min(w, h) / 2));\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // SVG mask: white = visible overlay, black = hole (transparent)\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${vw}\" height=\"${vh}\" viewBox=\"0 0 ${vw} ${vh}\">\n <defs>\n <mask id=\"spotlightMask\" maskUnits=\"userSpaceOnUse\">\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"white\"/>\n <rect x=\"${x}\" y=\"${y}\" width=\"${w}\" height=\"${h}\" rx=\"${r}\" ry=\"${r}\" fill=\"black\"/>\n </mask>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${vw}\" height=\"${vh}\" fill=\"black\" mask=\"url(#spotlightMask)\"/>\n </svg>`.trim();\n\n const encoded = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;\n\n return {\n WebkitMaskImage: `url(\"${encoded}\")`,\n WebkitMaskRepeat: 'no-repeat',\n WebkitMaskSize: `${vw}px ${vh}px`,\n maskImage: `url(\"${encoded}\")`,\n maskRepeat: 'no-repeat',\n maskSize: `${vw}px ${vh}px`,\n } as React.CSSProperties;\n };\n\n /** useEffect to update the target rect, so that the mask tracks the target element */\n useEffect(() => {\n if (active == null) return;\n\n const el = targets[active]?.current;\n if (el === null || el === undefined) return;\n\n const update = () => setTargetRect(el.getBoundingClientRect());\n update();\n\n const ro = new ResizeObserver(update);\n ro.observe(el);\n\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n\n return () => {\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [active, targets]);\n\n return (\n <SpotlightContext.Provider value={contextValue}>\n {hasOverlay && active !== null && isOpen && (\n <FloatingPortal>\n <FloatingOverlay\n lockScroll={true}\n className={overlayClasses}\n style={getMaskStyle()}\n data-testid=\"ndl-spotlight-overlay\"\n />\n </FloatingPortal>\n )}\n {children}\n </SpotlightContext.Provider>\n );\n};\n\nexport const useSpotlightContext = (): SpotlightContextType => {\n const context = useContext(SpotlightContext);\n\n if (context === null) {\n throw new Error(\n 'Spotlight components must be wrapped in <SpotlightProvider />',\n );\n }\n\n return context;\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SpotlightProvider.d.ts","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,KAAK,sBAAsB,GAAG;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,kFAAkF;IAClF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,sDAAsD;IACtD,MAAM,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,yGAAyG;IACzG,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,2IAA2I;IAC3I,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD,2HAA2H;IAC3H,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACxE,8FAA8F;IAC9F,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sIAAsI;IACtI,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;IAChE,iFAAiF;IACjF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAAI,iDAI/B,sBAAsB,4CAwJxB,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,oBAUtC,CAAC"}
1
+ {"version":3,"file":"SpotlightProvider.d.ts","sourceRoot":"","sources":["../../../src/spotlight/SpotlightProvider.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,KAAK,sBAAsB,GAAG;IAC5B,qCAAqC;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,kFAAkF;IAClF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,sDAAsD;IACtD,MAAM,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,yGAAyG;IACzG,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,2IAA2I;IAC3I,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACpD,2HAA2H;IAC3H,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACxE,8FAA8F;IAC9F,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,sIAAsI;IACtI,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;IAChE,iFAAiF;IACjF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAAI,iDAI/B,sBAAsB,4CAgKxB,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,oBAUtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-ndl/react",
3
- "version": "4.0.8",
3
+ "version": "4.0.9",
4
4
  "sideEffects": false,
5
5
  "description": "React implementation of Neo4j Design System",
6
6
  "keywords": [