@djangocfg/ui-tools 2.1.247 → 2.1.248
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/JsonTree-F27RMYSI.cjs +11 -0
- package/dist/{JsonTree-63J76I75.mjs.map → JsonTree-F27RMYSI.cjs.map} +1 -1
- package/dist/JsonTree-QTJYSHCV.mjs +5 -0
- package/dist/{JsonTree-BJVX7XUH.cjs.map → JsonTree-QTJYSHCV.mjs.map} +1 -1
- package/dist/{Mermaid.client-Z25LMNPA.cjs → Mermaid.client-RSWUUHIL.cjs} +6 -6
- package/dist/{Mermaid.client-Z25LMNPA.cjs.map → Mermaid.client-RSWUUHIL.cjs.map} +1 -1
- package/dist/{Mermaid.client-5EKULAUZ.mjs → Mermaid.client-XFQ74OYN.mjs} +3 -3
- package/dist/{Mermaid.client-5EKULAUZ.mjs.map → Mermaid.client-XFQ74OYN.mjs.map} +1 -1
- package/dist/{PlaygroundLayout-KE2ME6WH.cjs → PlaygroundLayout-FKXSULJ3.cjs} +21 -21
- package/dist/{PlaygroundLayout-KE2ME6WH.cjs.map → PlaygroundLayout-FKXSULJ3.cjs.map} +1 -1
- package/dist/{PlaygroundLayout-4TCSESTD.mjs → PlaygroundLayout-XMMHPZYP.mjs} +5 -5
- package/dist/{PlaygroundLayout-4TCSESTD.mjs.map → PlaygroundLayout-XMMHPZYP.mjs.map} +1 -1
- package/dist/{PrettyCode.client-K45ON7DD.mjs → PrettyCode.client-OO3KAJSM.mjs} +3 -3
- package/dist/{PrettyCode.client-K45ON7DD.mjs.map → PrettyCode.client-OO3KAJSM.mjs.map} +1 -1
- package/dist/{PrettyCode.client-EMBU27DJ.cjs → PrettyCode.client-V2ZN5DTH.cjs} +5 -5
- package/dist/{PrettyCode.client-EMBU27DJ.cjs.map → PrettyCode.client-V2ZN5DTH.cjs.map} +1 -1
- package/dist/{chunk-G2A6SX5L.cjs → chunk-2SMCH62O.cjs} +34 -82
- package/dist/chunk-2SMCH62O.cjs.map +1 -0
- package/dist/{chunk-TNTCWAVZ.cjs → chunk-33AMWFBZ.cjs} +7 -7
- package/dist/{chunk-TNTCWAVZ.cjs.map → chunk-33AMWFBZ.cjs.map} +1 -1
- package/dist/{chunk-N7EDSU3Z.cjs → chunk-CRHHUOVJ.cjs} +3 -3
- package/dist/{chunk-N7EDSU3Z.cjs.map → chunk-CRHHUOVJ.cjs.map} +1 -1
- package/dist/{chunk-C5PVPPR7.mjs → chunk-LFWQ36LJ.mjs} +3 -3
- package/dist/{chunk-C5PVPPR7.mjs.map → chunk-LFWQ36LJ.mjs.map} +1 -1
- package/dist/{chunk-MBFBVGUP.mjs → chunk-SSUOENAZ.mjs} +35 -83
- package/dist/chunk-SSUOENAZ.mjs.map +1 -0
- package/dist/{chunk-TSDCMPCW.mjs → chunk-SZ2CZEQZ.mjs} +3 -3
- package/dist/{chunk-TSDCMPCW.mjs.map → chunk-SZ2CZEQZ.mjs.map} +1 -1
- package/dist/index.cjs +14 -14
- package/dist/index.mjs +10 -10
- package/package.json +6 -6
- package/src/components/FloatingToolbar/index.tsx +36 -56
- package/dist/JsonTree-63J76I75.mjs +0 -5
- package/dist/JsonTree-BJVX7XUH.cjs +0 -11
- package/dist/chunk-G2A6SX5L.cjs.map +0 -1
- package/dist/chunk-MBFBVGUP.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/FloatingToolbar/hooks/useElementCorner.ts","../src/components/FloatingToolbar/hooks/useScrollIsolation.ts","../src/components/FloatingToolbar/index.tsx","../src/components/FloatingToolbar/actions/CopyAction.tsx","../src/components/FloatingToolbar/actions/DownloadAction.tsx","../src/components/FloatingToolbar/actions/ExpandAction.tsx","../src/components/FloatingToolbar/actions/FullscreenAction.tsx"],"names":["useState","useEffect","jsx","BUTTON_CLASS","Button"],"mappings":"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","file":"chunk-MBFBVGUP.mjs","sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\ninterface Corner {\n top: number;\n right: number;\n bottom: number;\n}\n\n/**\n * Tracks the top-right corner of a referenced element in viewport coordinates.\n * Returns { top, right } in px — ready for `position: fixed` toolbar placement.\n *\n * Uses `visualViewport` for accurate viewport width — correctly handles:\n * - DevTools panel open (docked left/right/bottom)\n * - Browser zoom\n * - Mobile virtual keyboard / pinch-zoom\n * - Scrollbars\n *\n * Falls back to `document.documentElement.clientWidth` (excludes scrollbars,\n * matches `position: fixed` coordinate space) when visualViewport is unavailable.\n *\n * Updates on:\n * - Any ancestor scroll (capture phase)\n * - visualViewport resize/scroll\n * - ResizeObserver on the element itself\n */\nexport function useElementCorner(ref: React.RefObject<HTMLElement | null>) {\n const [corner, setCorner] = useState<Corner | null>(null);\n const updateRef = useRef<() => void>(() => {});\n\n updateRef.current = () => {\n if (!ref.current) return;\n const rect = ref.current.getBoundingClientRect();\n\n // `position: fixed` is relative to the layout viewport (document.documentElement.clientWidth),\n // NOT window.innerWidth (which includes scrollbar gutter).\n // visualViewport.width is the visible area — same as layout viewport when no zoom/keyboard.\n const viewportWidth =\n window.visualViewport?.width ?? document.documentElement.clientWidth;\n\n setCorner({\n top: rect.top,\n right: viewportWidth - rect.right,\n bottom: rect.bottom,\n });\n };\n\n useEffect(() => {\n const handler = () => updateRef.current();\n\n handler();\n\n // Element size changes\n const ro = new ResizeObserver(handler);\n if (ref.current) ro.observe(ref.current);\n\n // Any ancestor scroll (capture catches all, including overflow:auto containers)\n window.addEventListener('scroll', handler, { capture: true, passive: true });\n\n // visualViewport handles: DevTools resize, browser zoom, mobile keyboard\n const vv = window.visualViewport;\n if (vv) {\n vv.addEventListener('resize', handler);\n vv.addEventListener('scroll', handler);\n } else {\n window.addEventListener('resize', handler, { passive: true });\n }\n\n return () => {\n ro.disconnect();\n window.removeEventListener('scroll', handler, { capture: true });\n if (vv) {\n vv.removeEventListener('resize', handler);\n vv.removeEventListener('scroll', handler);\n } else {\n window.removeEventListener('resize', handler);\n }\n };\n }, [ref]);\n\n return corner;\n}\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\n\nconst UNLOCKED_CLASS = 'scroll-unlocked';\n\n/**\n * Scroll isolation — prevents the container from capturing wheel scroll\n * until the user explicitly clicks inside it (like Google Maps).\n *\n * Locked (overlay visible): wheel events on container scroll the PAGE.\n * Unlocked: normal scroll inside the container.\n *\n * Unlock: click anywhere inside the container.\n * Re-lock: click outside the container.\n *\n * When unlocked, adds `scroll-unlocked` class to the container element\n * so it can be styled (e.g. focus ring) via CSS.\n */\nexport function useScrollIsolation(\n ref: React.RefObject<HTMLElement | null>,\n enabled: boolean,\n) {\n const [locked, setLocked] = useState(true);\n\n const unlock = useCallback(() => setLocked(false), []);\n\n // Re-lock when clicking outside the container\n useEffect(() => {\n if (!enabled) return;\n\n const handleDocClick = (e: MouseEvent) => {\n const el = ref.current;\n if (!el) return;\n if (!el.contains(e.target as Node)) {\n setLocked(true);\n }\n };\n\n document.addEventListener('click', handleDocClick, true);\n return () => document.removeEventListener('click', handleDocClick, true);\n }, [enabled, ref]);\n\n // Toggle class on container to allow CSS-driven focus ring\n useEffect(() => {\n const el = ref.current;\n if (!el || !enabled) return;\n if (locked) {\n el.classList.remove(UNLOCKED_CLASS);\n } else {\n el.classList.add(UNLOCKED_CLASS);\n }\n return () => el.classList.remove(UNLOCKED_CLASS);\n }, [locked, enabled, ref]);\n\n // Reset to locked when feature toggled on\n useEffect(() => {\n if (enabled) setLocked(true);\n }, [enabled]);\n\n return { locked, unlock };\n}\n","'use client';\n\nimport React, { useState } from 'react';\n\nimport './FloatingToolbar.css';\nimport { useElementCorner } from './hooks/useElementCorner';\nimport { useScrollIsolation } from './hooks/useScrollIsolation';\n\nexport interface FloatingToolbarProps {\n /** Ref to the container element the toolbar anchors to */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Action buttons to render (right side) */\n children: React.ReactNode;\n /** Optional label shown left of the buttons (e.g. language badge) */\n label?: React.ReactNode;\n /** Where to anchor relative to the container (default: bottom-right) */\n position?: 'top-right' | 'bottom-right';\n /** Offset from the edge in px (default: 8) */\n offset?: number;\n /** z-index (default: 30) */\n zIndex?: number;\n /**\n * Block wheel scroll inside the container until user clicks into it.\n * Re-locks when mouse leaves. Like Google Maps scroll isolation.\n * @default true\n */\n scrollIsolation?: boolean;\n}\n\nconst MIN_HEIGHT = 40; // minimum container height to show toolbar\n\nexport const FloatingToolbar: React.FC<FloatingToolbarProps> = ({\n containerRef,\n children,\n label,\n position = 'bottom-right',\n offset = 8,\n zIndex = 30,\n scrollIsolation = true,\n}) => {\n const corner = useElementCorner(containerRef);\n const { locked, unlock } = useScrollIsolation(containerRef, scrollIsolation);\n const [overlayHovered, setOverlayHovered] = useState(false);\n\n // Overlay always renders (even before corner is measured) — it's position:absolute\n // inside the container so it works immediately on mount.\n // It absorbs wheel events (pointer-events cover the container) so the page scrolls normally.\n // Click anywhere inside unlocks it; click outside re-locks it.\n const overlay =\n scrollIsolation && locked ? (\n <div\n onClick={unlock}\n onMouseEnter={() => setOverlayHovered(true)}\n onMouseLeave={() => setOverlayHovered(false)}\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: zIndex - 1,\n cursor: 'pointer',\n background: overlayHovered ? 'rgba(0,0,0,0.04)' : 'transparent',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'background 150ms',\n }}\n >\n {overlayHovered && (\n <span style={{\n fontSize: '0.75rem',\n padding: '0.25rem 0.625rem',\n borderRadius: '9999px',\n background: 'rgba(0,0,0,0.55)',\n color: '#fff',\n pointerEvents: 'none',\n userSelect: 'none',\n }}>\n Click to scroll\n </span>\n )}\n </div>\n ) : null;\n\n // Toolbar is position:fixed and needs measured coords — skip until ready\n let toolbar: React.ReactNode = null;\n if (corner) {\n const { top, right, bottom } = corner;\n const viewportHeight = window.visualViewport?.height ?? document.documentElement.clientHeight;\n\n const inView = bottom > 0 && top < viewportHeight && bottom - top >= MIN_HEIGHT;\n\n if (inView) {\n const style: React.CSSProperties = {\n position: 'fixed',\n right: right + offset,\n zIndex,\n };\n\n if (position === 'bottom-right') {\n const clampedBottom = Math.min(bottom - offset, viewportHeight - offset);\n style.bottom = viewportHeight - clampedBottom;\n } else {\n if (top >= 0) {\n style.top = top + offset;\n } else {\n // top of block scrolled above viewport — hide toolbar\n }\n }\n\n if (style.bottom !== undefined || style.top !== undefined) {\n toolbar = (\n <div className=\"flex items-center gap-1\" style={style}>\n {label && (\n <>\n {label}\n <div className=\"w-px h-4 bg-border/50 mx-0.5\" />\n </>\n )}\n {children}\n </div>\n );\n }\n }\n }\n\n return (\n <>\n {overlay}\n {toolbar}\n </>\n );\n};\n","'use client';\n\nimport React from 'react';\nimport { CopyButton } from '@djangocfg/ui-core/components';\n\ninterface CopyActionProps {\n value: string;\n title?: string;\n}\n\nconst BUTTON_CLASS = 'h-6 w-6 rounded-sm bg-muted/80 hover:bg-muted border border-border/50 backdrop-blur-sm';\n\nexport const CopyAction: React.FC<CopyActionProps> = ({ value, title = 'Copy' }) => (\n <CopyButton\n value={value}\n variant=\"ghost\"\n size=\"icon\"\n className={BUTTON_CLASS}\n iconClassName=\"h-3 w-3\"\n title={title}\n />\n);\n","'use client';\n\nimport { Download } from 'lucide-react';\nimport React from 'react';\n\nimport { Button } from '@djangocfg/ui-core/components';\n\ninterface DownloadActionProps {\n value: string;\n filename?: string;\n mimeType?: string;\n title?: string;\n}\n\nconst BUTTON_CLASS = 'h-6 w-6 rounded-sm bg-muted/80 hover:bg-muted border border-border/50 backdrop-blur-sm';\n\nexport const DownloadAction: React.FC<DownloadActionProps> = ({\n value,\n filename = 'download.txt',\n mimeType = 'text/plain',\n title = 'Download',\n}) => {\n const handleDownload = () => {\n const blob = new Blob([value], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n };\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleDownload}\n className={BUTTON_CLASS}\n title={title}\n >\n <Download className=\"h-3 w-3\" />\n </Button>\n );\n};\n","'use client';\n\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport React from 'react';\n\nimport { Button } from '@djangocfg/ui-core/components';\n\ninterface ExpandActionProps {\n isExpanded: boolean;\n onToggle: () => void;\n}\n\nconst BUTTON_CLASS = 'h-6 w-6 rounded-sm bg-muted/80 hover:bg-muted border border-border/50 backdrop-blur-sm';\n\nexport const ExpandAction: React.FC<ExpandActionProps> = ({ isExpanded, onToggle }) => (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onToggle}\n className={BUTTON_CLASS}\n title={isExpanded ? 'Collapse All' : 'Expand All'}\n >\n {isExpanded ? <ChevronUp className=\"h-3 w-3\" /> : <ChevronDown className=\"h-3 w-3\" />}\n </Button>\n);\n","'use client';\n\nimport { Maximize2, Minimize2 } from 'lucide-react';\nimport React from 'react';\n\nimport { Button } from '@djangocfg/ui-core/components';\n\ninterface FullscreenActionProps {\n isFullscreen?: boolean;\n onToggle: () => void;\n title?: string;\n}\n\nconst BUTTON_CLASS = 'h-6 w-6 rounded-sm bg-muted/80 hover:bg-muted border border-border/50 backdrop-blur-sm';\n\nexport const FullscreenAction: React.FC<FullscreenActionProps> = ({\n isFullscreen = false,\n onToggle,\n title,\n}) => (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onToggle}\n className={BUTTON_CLASS}\n title={title ?? (isFullscreen ? 'Exit fullscreen' : 'Fullscreen')}\n >\n {isFullscreen ? <Minimize2 className=\"h-3 w-3\" /> : <Maximize2 className=\"h-3 w-3\" />}\n </Button>\n);\n"]}
|