@ahrowe/ui 0.36.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/cjs-types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/cjs-types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/cjs-types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/cjs-types/common/commandPalette/index.d.ts +2 -0
- package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/cjs-types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/cjs-types/common/copyButton/index.d.ts +2 -0
- package/dist/cjs-types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/cjs-types/common/hooks/index.d.ts +15 -0
- package/dist/cjs-types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/cjs-types/common/hooks/useHotkeys.d.ts +45 -0
- package/dist/cjs-types/common/hooks/useSwipeDismiss.d.ts +6 -2
- package/dist/cjs-types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/cjs-types/common/loading/index.d.ts +0 -1
- package/dist/cjs-types/common/menu/index.d.ts +1 -0
- package/dist/cjs-types/common/menu/menu.types.d.ts +11 -3
- package/dist/cjs-types/common/menu/menuHotkeys.d.ts +20 -0
- package/dist/cjs-types/common/popover/usePopoverPosition.d.ts +1 -0
- package/dist/cjs-types/common/sticky/sticky.d.ts +1 -1
- package/dist/cjs-types/common/sticky/sticky.types.d.ts +15 -0
- package/dist/cjs-types/common/sticky/stickyStack.d.ts +5 -0
- package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/cjs-types/common/utils/scrollAncestors.d.ts +10 -0
- package/dist/cjs-types/common/virtualList/scroller.d.ts +33 -0
- package/dist/cjs-types/common/virtualList/useRowDrag.d.ts +6 -1
- package/dist/cjs-types/common/virtualList/useRowNavigation.d.ts +11 -1
- package/dist/cjs-types/common/virtualList/useVirtualWindow.d.ts +22 -3
- package/dist/cjs-types/common/virtualList/virtualList.d.ts +1 -1
- package/dist/cjs-types/common/virtualList/virtualList.types.d.ts +43 -3
- package/dist/cjs-types/common/weatherIcon/index.d.ts +3 -0
- package/dist/cjs-types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/cjs-types/index.d.ts +7 -0
- package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs.map +1 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs +2 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -0
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
- package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs.map +1 -1
- package/dist/esm/common/hooks/useClipboard.mjs +2 -0
- package/dist/esm/common/hooks/useClipboard.mjs.map +1 -0
- package/dist/esm/common/hooks/useHotkeys.mjs +2 -0
- package/dist/esm/common/hooks/useHotkeys.mjs.map +1 -0
- package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
- package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
- package/dist/esm/common/input/input.mjs +1 -1
- package/dist/esm/common/input/input.mjs.map +1 -1
- package/dist/esm/common/input/input.module.mjs +1 -1
- package/dist/esm/common/input/input.module.mjs.map +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
- package/dist/esm/common/menu/menu.mjs +1 -1
- package/dist/esm/common/menu/menu.mjs.map +1 -1
- package/dist/esm/common/menu/menu.types.mjs.map +1 -1
- package/dist/esm/common/menu/menuHotkeys.mjs +2 -0
- package/dist/esm/common/menu/menuHotkeys.mjs.map +1 -0
- package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
- package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
- package/dist/esm/common/multiSelect/multiSelect.module.mjs +1 -1
- package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -1
- package/dist/esm/common/otpInput/otpInput.module.mjs.map +1 -1
- package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
- package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
- package/dist/esm/common/sticky/sticky.mjs +1 -1
- package/dist/esm/common/sticky/sticky.mjs.map +1 -1
- package/dist/esm/common/sticky/stickyStack.mjs +1 -1
- package/dist/esm/common/sticky/stickyStack.mjs.map +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/utils/scrollAncestors.mjs +1 -1
- package/dist/esm/common/utils/scrollAncestors.mjs.map +1 -1
- package/dist/esm/common/virtualList/scroller.mjs +2 -0
- package/dist/esm/common/virtualList/scroller.mjs.map +1 -0
- package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
- package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
- package/dist/esm/common/virtualList/useRowNavigation.mjs +1 -1
- package/dist/esm/common/virtualList/useRowNavigation.mjs.map +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.module.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.module.mjs.map +1 -1
- package/dist/esm/common/weatherIcon/parts.mjs +2 -0
- package/dist/esm/common/weatherIcon/parts.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/types/common/commandPalette/index.d.ts +2 -0
- package/dist/types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/types/common/copyButton/index.d.ts +2 -0
- package/dist/types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/types/common/hooks/index.d.ts +15 -0
- package/dist/types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/types/common/hooks/useHotkeys.d.ts +45 -0
- package/dist/types/common/hooks/useSwipeDismiss.d.ts +6 -2
- package/dist/types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/types/common/loading/index.d.ts +0 -1
- package/dist/types/common/menu/index.d.ts +1 -0
- package/dist/types/common/menu/menu.types.d.ts +11 -3
- package/dist/types/common/menu/menuHotkeys.d.ts +20 -0
- package/dist/types/common/popover/usePopoverPosition.d.ts +1 -0
- package/dist/types/common/sticky/sticky.d.ts +1 -1
- package/dist/types/common/sticky/sticky.types.d.ts +15 -0
- package/dist/types/common/sticky/stickyStack.d.ts +5 -0
- package/dist/types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/types/common/utils/scrollAncestors.d.ts +10 -0
- package/dist/types/common/virtualList/scroller.d.ts +33 -0
- package/dist/types/common/virtualList/useRowDrag.d.ts +6 -1
- package/dist/types/common/virtualList/useRowNavigation.d.ts +11 -1
- package/dist/types/common/virtualList/useVirtualWindow.d.ts +22 -3
- package/dist/types/common/virtualList/virtualList.d.ts +1 -1
- package/dist/types/common/virtualList/virtualList.types.d.ts +43 -3
- package/dist/types/common/weatherIcon/index.d.ts +3 -0
- package/dist/types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/types/index.d.ts +7 -0
- package/docs/ButtonGroup.md +2 -0
- package/docs/CLAUDE.md +6 -2
- package/docs/CommandPalette.md +84 -0
- package/docs/ConfigProvider.md +2 -2
- package/docs/CopyButton.md +45 -0
- package/docs/ErrorBoundary.md +4 -4
- package/docs/Fab.md +1 -2
- package/docs/Hooks.md +268 -0
- package/docs/InputDropdown.md +9 -4
- package/docs/Loading.md +56 -15
- package/docs/Menu.md +30 -6
- package/docs/Popover.md +2 -0
- package/docs/Sticky.md +22 -1
- package/docs/VirtualList.md +44 -4
- package/docs/WeatherIcon.md +49 -0
- package/package.json +1 -2
- package/dist/cjs-types/common/loading/iconLoading/iconLoading.d.ts +0 -5
- package/dist/cjs-types/common/loading/iconLoading/index.d.ts +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.mjs.map +0 -1
- package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs.map +0 -1
- package/dist/esm/common/loading/iconLoading/index.mjs +0 -2
- package/dist/esm/common/loading/iconLoading/index.mjs.map +0 -1
- package/dist/types/common/loading/iconLoading/iconLoading.d.ts +0 -5
- package/dist/types/common/loading/iconLoading/index.d.ts +0 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import e from"./useCoarsePointer.mjs";import{
|
|
1
|
+
"use client";import e from"./useCoarsePointer.mjs";import{useCallback as t,useEffect as n,useRef as r,useState as i}from"react";var a=180,o=10;function s({onDismiss:s,axis:c=`x`,distanceThreshold:l=80,velocityThreshold:u=.5,disabled:d=!1,mobileOnly:f=!0}){let[p,m]=i(null),h=t(e=>m(e),[]),[g,_]=i(!1),v=e(),y=d||f&&!v,b=r(null),x=r(s);return n(()=>{x.current=s}),n(()=>{let e=p;if(!e||y)return;function t(t,n){if(!e)return;n!==`none`&&e.offsetHeight;let r=c===`x`?`translateX(${t}px)`:`translateY(${t}px)`;e.style.transition=n,e.style.transform=r;let i=Math.max(0,1-Math.abs(t)/(l*2.5));e.style.opacity=String(i)}function n(e){let n=b.current;if(!n||e.pointerId!==n.pointerId)return;let r=e.clientX-n.startX,i=e.clientY-n.startY;if(!n.axisLocked){let e=c===`x`?r:i,t=c===`x`?i:r;if(Math.abs(e)<o&&Math.abs(t)<o)return;if(Math.abs(t)>Math.abs(e)){n.active=!1;return}n.axisLocked=!0,_(!0)}n.active&&(e.preventDefault(),n.offset=c===`x`?r:i,t(n.offset,`none`))}function r(){let i=b.current;if(b.current=null,window.removeEventListener(`pointermove`,n),window.removeEventListener(`pointerup`,r),window.removeEventListener(`pointercancel`,r),!i||!i.active||!i.axisLocked){_(!1);return}let o=Math.max(1,Date.now()-i.startTime),s=i.offset/o;if(Math.abs(i.offset)>=l||Math.abs(s)>=u){let n=e?.getBoundingClientRect().width??0;t(Math.sign(i.offset||1)*(n+40),`transform ${a}ms ease-out, opacity ${a}ms ease-out`),setTimeout(()=>x.current(),a)}else t(0,`transform ${a}ms ease-out, opacity ${a}ms ease-out`);_(!1)}function i(t){!b.current&&e&&(e.style.animation=`none`,b.current={pointerId:t.pointerId,startX:t.clientX,startY:t.clientY,startTime:Date.now(),offset:0,axisLocked:!1,active:!0},window.addEventListener(`pointermove`,n,{passive:!1}),window.addEventListener(`pointerup`,r),window.addEventListener(`pointercancel`,r))}return e.addEventListener(`pointerdown`,i),()=>{e.removeEventListener(`pointerdown`,i),window.removeEventListener(`pointermove`,n),window.removeEventListener(`pointerup`,r),window.removeEventListener(`pointercancel`,r)}},[p,c,l,u,y]),{ref:h,isDragging:g}}export{s as useSwipeDismiss};
|
|
2
2
|
//# sourceMappingURL=useSwipeDismiss.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSwipeDismiss.mjs","names":[],"sources":["../../../../package/common/hooks/useSwipeDismiss.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport { useCoarsePointer } from './useCoarsePointer';\n\nexport interface UseSwipeDismissOptions {\n /** Called once the swipe commits (past the threshold) — after the exit transition finishes,\n * not the instant the pointer is released. */\n onDismiss: () => void;\n /** Which axis the drag tracks (default `'x'`, the natural direction for a toast/notification). */\n axis?: 'x' | 'y';\n /** Distance in px past which releasing counts as a commit, regardless of speed (default `80`). */\n distanceThreshold?: number;\n /** Speed in px/ms past which releasing counts as a commit, regardless of distance (default `0.5`) —\n * lets a fast flick dismiss even if the pointer didn't travel very far. */\n velocityThreshold?: number;\n /** Disables the gesture entirely — no listeners are attached. */\n disabled?: boolean;\n /** Restricts the gesture to coarse (touch) pointers, auto-detected via `matchMedia`. On a\n * mouse/trackpad device a drag is more likely to be a text selection than an intentional\n * dismiss, and other affordances (a close button, auto-dismiss) typically already cover that\n * case. Default `true`; set `false` to allow the gesture on any pointer type. */\n mobileOnly?: boolean;\n}\n\nexport interface UseSwipeDismissResult<T extends HTMLElement> {\n /** Attach to the element that should become draggable. The hook wires up its own pointer\n * listeners on this node directly (no handlers to spread), so this is the only thing to attach. */\n ref: React.RefObject<T | null>;\n /** True for the duration of an active drag — e.g. use it to pause an auto-dismiss timer. */\n isDragging: boolean;\n}\n\nconst EXIT_TRANSITION_MS = 180;\n// Below this many px of movement, direction intent isn't decided yet — lets a vertical scroll\n// gesture that merely starts on the element pass through instead of being hijacked as a swipe.\nconst AXIS_LOCK_THRESHOLD = 10;\n\n/**\n * Drag-to-dismiss on any pointer type (touch, mouse, pen) via the Pointer Events API. Applies the\n * drag offset and a fading opacity directly to the DOM node (bypassing React state) so the visual\n * tracks the pointer at native speed, then either springs back or animates fully off-screen and\n * calls `onDismiss` once that exit transition completes.\n */\nexport function useSwipeDismiss<T extends HTMLElement>({\n onDismiss,\n axis = 'x',\n distanceThreshold = 80,\n velocityThreshold = 0.5,\n disabled = false,\n mobileOnly = true,\n}: UseSwipeDismissOptions): UseSwipeDismissResult<T> {\n const ref = useRef<T>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n const isCoarsePointer = useCoarsePointer();\n\n const effectiveDisabled = disabled || (mobileOnly && !isCoarsePointer);\n\n // Mutable across the life of one drag; doesn't need to trigger re-renders.\n const dragRef = useRef<{\n pointerId: number;\n startX: number;\n startY: number;\n startTime: number;\n offset: number;\n axisLocked: boolean;\n active: boolean;\n } | null>(null);\n\n const onDismissRef = useRef(onDismiss);\n useEffect(() => {\n onDismissRef.current = onDismiss;\n });\n\n useEffect(() => {\n const el = ref.current;\n if (!el || effectiveDisabled) return;\n\n function applyTransform(offset: number, transition: string) {\n if (!el) return;\n if (transition !== 'none') {\n // Switching straight from the live drag's `transition: none` to an animated one, in the\n // same tick as the transform change, can get coalesced into a single frame with no\n // animation in between — the element just jumps to its end state instead of sliding\n // there. Forcing a layout read first commits the current (pre-transition) position as\n // its own frame, so the transition below actually has something to animate from.\n void el.offsetHeight;\n }\n const translate = axis === 'x' ? `translateX(${offset}px)` : `translateY(${offset}px)`;\n el.style.transition = transition;\n el.style.transform = translate;\n const fade = Math.max(0, 1 - Math.abs(offset) / (distanceThreshold * 2.5));\n el.style.opacity = String(fade);\n }\n\n function onPointerMove(event: PointerEvent) {\n const drag = dragRef.current;\n if (!drag || event.pointerId !== drag.pointerId) return;\n\n const deltaX = event.clientX - drag.startX;\n const deltaY = event.clientY - drag.startY;\n\n if (!drag.axisLocked) {\n const primary = axis === 'x' ? deltaX : deltaY;\n const cross = axis === 'x' ? deltaY : deltaX;\n if (Math.abs(primary) < AXIS_LOCK_THRESHOLD && Math.abs(cross) < AXIS_LOCK_THRESHOLD) return;\n if (Math.abs(cross) > Math.abs(primary)) {\n // The gesture is mostly along the other axis (e.g. a page scroll) — hand it back.\n drag.active = false;\n return;\n }\n drag.axisLocked = true;\n setIsDragging(true);\n }\n\n if (!drag.active) return;\n event.preventDefault();\n drag.offset = axis === 'x' ? deltaX : deltaY;\n applyTransform(drag.offset, 'none');\n }\n\n function finishDrag() {\n const drag = dragRef.current;\n dragRef.current = null;\n window.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', finishDrag);\n window.removeEventListener('pointercancel', finishDrag);\n if (!drag || !drag.active || !drag.axisLocked) {\n setIsDragging(false);\n return;\n }\n\n const elapsed = Math.max(1, Date.now() - drag.startTime);\n const velocity = drag.offset / elapsed;\n const committed = Math.abs(drag.offset) >= distanceThreshold || Math.abs(velocity) >= velocityThreshold;\n\n if (committed) {\n const width = el?.getBoundingClientRect().width ?? 0;\n const exitOffset = Math.sign(drag.offset || 1) * (width + 40);\n applyTransform(\n exitOffset,\n `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`,\n );\n setTimeout(() => onDismissRef.current(), EXIT_TRANSITION_MS);\n } else {\n applyTransform(0, `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`);\n }\n setIsDragging(false);\n }\n\n function onPointerDown(event: PointerEvent) {\n if (dragRef.current || !el) return;\n // A still-\"forwards\"-filling CSS animation (e.g. the toast's entrance slide) wins the\n // cascade over an inline style.transform, so the drag offset below would silently never\n // show up on screen unless the animation is cleared first.\n el.style.animation = 'none';\n dragRef.current = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n startTime: Date.now(),\n offset: 0,\n axisLocked: false,\n active: true,\n };\n window.addEventListener('pointermove', onPointerMove, { passive: false });\n window.addEventListener('pointerup', finishDrag);\n window.addEventListener('pointercancel', finishDrag);\n }\n\n el.addEventListener('pointerdown', onPointerDown);\n return () => {\n el.removeEventListener('pointerdown', onPointerDown);\n window.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', finishDrag);\n window.removeEventListener('pointercancel', finishDrag);\n };\n }, [axis, distanceThreshold, velocityThreshold, effectiveDisabled]);\n\n return { ref, isDragging };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSwipeDismiss.mjs","names":[],"sources":["../../../../package/common/hooks/useSwipeDismiss.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { useCoarsePointer } from './useCoarsePointer';\n\nexport interface UseSwipeDismissOptions {\n /** Called once the swipe commits (past the threshold) — after the exit transition finishes,\n * not the instant the pointer is released. */\n onDismiss: () => void;\n /** Which axis the drag tracks (default `'x'`, the natural direction for a toast/notification). */\n axis?: 'x' | 'y';\n /** Distance in px past which releasing counts as a commit, regardless of speed (default `80`). */\n distanceThreshold?: number;\n /** Speed in px/ms past which releasing counts as a commit, regardless of distance (default `0.5`) —\n * lets a fast flick dismiss even if the pointer didn't travel very far. */\n velocityThreshold?: number;\n /** Disables the gesture entirely — no listeners are attached. */\n disabled?: boolean;\n /** Restricts the gesture to coarse (touch) pointers, auto-detected via `matchMedia`. On a\n * mouse/trackpad device a drag is more likely to be a text selection than an intentional\n * dismiss, and other affordances (a close button, auto-dismiss) typically already cover that\n * case. Default `true`; set `false` to allow the gesture on any pointer type. */\n mobileOnly?: boolean;\n}\n\nexport interface UseSwipeDismissResult<T extends HTMLElement> {\n /** Attach to the element that should become draggable. The hook wires up its own pointer\n * listeners on this node directly (no handlers to spread), so this is the only thing to attach.\n *\n * A callback ref rather than an object one, because the hook has to know when the node changes:\n * an element that is unmounted and rendered again is a *different* node, and a ref object cannot\n * say so. To keep a ref of your own on the element, call both from one callback. */\n ref: React.RefCallback<T>;\n /** True for the duration of an active drag — e.g. use it to pause an auto-dismiss timer. */\n isDragging: boolean;\n}\n\nconst EXIT_TRANSITION_MS = 180;\n// Below this many px of movement, direction intent isn't decided yet — lets a vertical scroll\n// gesture that merely starts on the element pass through instead of being hijacked as a swipe.\nconst AXIS_LOCK_THRESHOLD = 10;\n\n/**\n * Drag-to-dismiss on any pointer type (touch, mouse, pen) via the Pointer Events API. Applies the\n * drag offset and a fading opacity directly to the DOM node (bypassing React state) so the visual\n * tracks the pointer at native speed, then either springs back or animates fully off-screen and\n * calls `onDismiss` once that exit transition completes.\n */\nexport function useSwipeDismiss<T extends HTMLElement>({\n onDismiss,\n axis = 'x',\n distanceThreshold = 80,\n velocityThreshold = 0.5,\n disabled = false,\n mobileOnly = true,\n}: UseSwipeDismissOptions): UseSwipeDismissResult<T> {\n // The node lives in state, not in a ref: the listeners have to be re-attached when the element\n // is replaced, and a ref object's `current` changing never re-runs the effect below. Rendering\n // the element again after it was unmounted otherwise left the gesture wired to the old node.\n const [node, setNode] = useState<T | null>(null);\n const ref = useCallback((element: T | null) => setNode(element), []);\n const [isDragging, setIsDragging] = useState(false);\n\n const isCoarsePointer = useCoarsePointer();\n\n const effectiveDisabled = disabled || (mobileOnly && !isCoarsePointer);\n\n // Mutable across the life of one drag; doesn't need to trigger re-renders.\n const dragRef = useRef<{\n pointerId: number;\n startX: number;\n startY: number;\n startTime: number;\n offset: number;\n axisLocked: boolean;\n active: boolean;\n } | null>(null);\n\n const onDismissRef = useRef(onDismiss);\n useEffect(() => {\n onDismissRef.current = onDismiss;\n });\n\n useEffect(() => {\n const el = node;\n if (!el || effectiveDisabled) return;\n\n function applyTransform(offset: number, transition: string) {\n if (!el) return;\n if (transition !== 'none') {\n // Switching straight from the live drag's `transition: none` to an animated one, in the\n // same tick as the transform change, can get coalesced into a single frame with no\n // animation in between — the element just jumps to its end state instead of sliding\n // there. Forcing a layout read first commits the current (pre-transition) position as\n // its own frame, so the transition below actually has something to animate from.\n void el.offsetHeight;\n }\n const translate = axis === 'x' ? `translateX(${offset}px)` : `translateY(${offset}px)`;\n el.style.transition = transition;\n el.style.transform = translate;\n const fade = Math.max(0, 1 - Math.abs(offset) / (distanceThreshold * 2.5));\n el.style.opacity = String(fade);\n }\n\n function onPointerMove(event: PointerEvent) {\n const drag = dragRef.current;\n if (!drag || event.pointerId !== drag.pointerId) return;\n\n const deltaX = event.clientX - drag.startX;\n const deltaY = event.clientY - drag.startY;\n\n if (!drag.axisLocked) {\n const primary = axis === 'x' ? deltaX : deltaY;\n const cross = axis === 'x' ? deltaY : deltaX;\n if (Math.abs(primary) < AXIS_LOCK_THRESHOLD && Math.abs(cross) < AXIS_LOCK_THRESHOLD) return;\n if (Math.abs(cross) > Math.abs(primary)) {\n // The gesture is mostly along the other axis (e.g. a page scroll) — hand it back.\n drag.active = false;\n return;\n }\n drag.axisLocked = true;\n setIsDragging(true);\n }\n\n if (!drag.active) return;\n event.preventDefault();\n drag.offset = axis === 'x' ? deltaX : deltaY;\n applyTransform(drag.offset, 'none');\n }\n\n function finishDrag() {\n const drag = dragRef.current;\n dragRef.current = null;\n window.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', finishDrag);\n window.removeEventListener('pointercancel', finishDrag);\n if (!drag || !drag.active || !drag.axisLocked) {\n setIsDragging(false);\n return;\n }\n\n const elapsed = Math.max(1, Date.now() - drag.startTime);\n const velocity = drag.offset / elapsed;\n const committed = Math.abs(drag.offset) >= distanceThreshold || Math.abs(velocity) >= velocityThreshold;\n\n if (committed) {\n const width = el?.getBoundingClientRect().width ?? 0;\n const exitOffset = Math.sign(drag.offset || 1) * (width + 40);\n applyTransform(\n exitOffset,\n `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`,\n );\n setTimeout(() => onDismissRef.current(), EXIT_TRANSITION_MS);\n } else {\n applyTransform(0, `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`);\n }\n setIsDragging(false);\n }\n\n function onPointerDown(event: PointerEvent) {\n if (dragRef.current || !el) return;\n // A still-\"forwards\"-filling CSS animation (e.g. the toast's entrance slide) wins the\n // cascade over an inline style.transform, so the drag offset below would silently never\n // show up on screen unless the animation is cleared first.\n el.style.animation = 'none';\n dragRef.current = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n startTime: Date.now(),\n offset: 0,\n axisLocked: false,\n active: true,\n };\n window.addEventListener('pointermove', onPointerMove, { passive: false });\n window.addEventListener('pointerup', finishDrag);\n window.addEventListener('pointercancel', finishDrag);\n }\n\n el.addEventListener('pointerdown', onPointerDown);\n return () => {\n el.removeEventListener('pointerdown', onPointerDown);\n window.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', finishDrag);\n window.removeEventListener('pointercancel', finishDrag);\n };\n }, [node, axis, distanceThreshold, velocityThreshold, effectiveDisabled]);\n\n return { ref, isDragging };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../tooltip/tooltip.mjs";import ee from"../animatedIcon/animatedIcon.mjs";import r from"./input.module.mjs";import{mergeRefs as te}from"../utils/mergeRefs.mjs";import{InputIconPosition as i,InputType as a}from"./input.types.mjs";import ne,{useCallback as re,useEffect as o,useId as ie,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as u}from"@fortawesome/react-fontawesome";import{faEnvelope as ae,faEye as oe,faEyeSlash as se,faPen as ce,faPhone as le,faSearch as ue,faXmark as de}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";var fe={clear:`Clear`,showPassword:`Show password`,hidePassword:`Hide password`,clearSearch:`Clear search`};function p(e){if(e)return{role:`button`,tabIndex:0,onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),e(t))}}}function pe(pe){let{name:me=``,id:he,label:m=``,errorMessage:h=``,isValid:ge=!0,className:_e=``,style:ve,value:ye=``,icon:g=null,iconPosition:be,onChange:_=()=>{},onFocus:xe=null,onBlur:Se=null,onMouseEnter:Ce=null,onMouseLeave:we=null,isUppercase:Te=!1,isRequired:Ee=!1,tabIndex:De=0,type:v=void 0,isRow:Oe=!1,onIconClick:y=null,parse:ke=null,formValidator:b=null,autoFocus:Ae=!1,suffix:je=null,upperRightLabel:x=null,labelInBorder:Me=!1,showCancel:Ne=!1,onClearClicked:Pe,min:Fe=null,max:Ie=null,placeholder:S=``,useMatLabelStyle:C=!0,alwaysFloatLabel:Le=!1,autoComplete:Re=null,onClick:ze=()=>null,readOnly:w=!1,customInput:T,alwaysShowCancel:Be=!1,multiline:E=!1,rows:Ve,autoResize:D=!1,classNames:O,styles:k,disabled:He=!1,labels:Ue,ref:We,...A}=e(`Input`,pe),j=t(`Input`,Ue,fe),[M,Ge]=c(!1),[Ke,N]=c(!1),[P,F]=c(!1),[I,L]=c(!1),[,qe]=c(0),R=s(null),z=s(b),Je=ie(),Ye=he||me||Je,B=b?b.value:ye,Xe=s(!1),V=re(()=>{let e=R.current;if(e){if(!D){Xe.current&&=(e.style.height=``,!1);return}e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`,Xe.current=!0}},[D]);o(()=>{V()},[V,B]),o(()=>{Ae&&R.current&&setTimeout(()=>{R.current?.focus()},1)},[Ae]),o(()=>{z.current=b;let e=z.current;if(!e)return;e.label=m??``,e.validate();let t=()=>qe(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[b,m]);let Ze=()=>b?b.touched&&b.hasError():!ge||!!h,Qe=()=>!!(Ee||b&&b.validators.find(e=>e.validatorId===`required`)),$e=e=>{F(!0),Ce&&Ce(e)},et=e=>{F(!1),we&&we(e)},tt=e=>{Ge(!0),xe&&xe(e)},nt=e=>{z.current&&(z.current.touched=!0),Ge(!1),Se&&Se(e)},rt=e=>{let t,n;typeof e==`string`?n=e:(t=e,n=e.target.value),ke&&(n=ke(n)),b&&b.set(n),_&&_(n,t),V()},it=()=>{if(v!==a.Email)return v===a.Phone?`tel`:v===a.Search?`search`:v===`password`&&I?`text`:v},at=()=>{if(v===a.Email)return`email`;if(v===a.Phone)return`tel`;if(v===a.Search)return`search`},ot=v&&v!==a.Date&&v!==a.Iban&&v!==a.HasNoIcon,st=v===a.Search||v===a.Password,H=!!g,ct=H&&!st,lt=v===a.Email||v===a.Phone||v===a.Search,U=(be??(!C&<?i.Left:i.Right))===i.Left,W={className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!y,[r.inputFieldIconLeft]:U}),onClick:y??void 0,...p(y)},G=g,K=y??G?.props.onClick,ut=ct&&G?ne.cloneElement(G,{className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!K,[r.inputFieldIconLeft]:U},G.props.className),onClick:K,...p(K)}):null,dt=(ot||H)&&U,ft={className:l(r.inputFieldIcon,r.inputFieldIconPassword,{[r.inputFieldIconLeft]:U}),onClick:()=>L(!I),...p(()=>L(!I)),"aria-label":I?j.hidePassword:j.showPassword},q=Ze(),J=m;J&&Qe()&&(J=`${J} *`);let pt=Qe()?d(`span`,{className:r.requiredMark,children:`*`}):null,Y=M&&!w,X=C&&(Le||Y||B||Ke||v===a.Date),mt=e=>{let{animationName:t}=e;if(t===r.inputAutofillStart){N(!0);let t=e.currentTarget.value;t&&t!==String(B??``)&&rt(t);return}if(t===r.inputAutofillEnd){N(!1);return}A.onAnimationStart?.(e)},Z=v===a.Search&&!!B,Q=T?.props;function $(){b&&b.set(``),_?.(``),Pe?.()}return f(`div`,{className:l(r.input,{[r.inputIconLeft]:dt},_e),style:ve,children:[f(`label`,{htmlFor:Ye,className:l(r.inputLabel,O?.label,{[r.inputLabelColumn]:!Oe,[r.inputLabelInBorder]:Me}),style:k?.label,children:[m&&!C?f(`span`,{className:r.inputLabelText,children:[m,pt]}):null,x&&d(`div`,{className:l(r.inputUpperRightLabel,O?.upperRightLabel),style:k?.upperRightLabel,children:x}),f(`div`,{className:l(r.inputContainer,{[r.inputContainerMultiline]:E},O?.container),style:k?.container,onMouseEnter:$e,onMouseLeave:et,children:[ut,ne.cloneElement(T||d(E?`textarea`:`input`,{}),{...A,className:l(r.inputField,{[r.inputFieldUppercase]:Te,[r.inputFieldDate]:v===a.Date,[r.inputFieldDefault]:v!==a.Date,[r.inputFieldWithIcon]:ot||H,[r.inputFieldWithIconLeft]:dt,[r.inputFieldMultiline]:E,[r.inputFieldMultilineAutoResize]:E&&D,[r.inputFieldReadOnly]:w&&!He},O?.input,Q?.className),style:Q?.style||k?.input?{...Q?.style,...k?.input}:void 0,name:me,id:Ye,value:B,tabIndex:De,ref:T?void 0:te(R,We),onChange:rt,onAnimationStart:mt,onFocus:tt,onBlur:nt,placeholder:C?``:S,autoComplete:Re,onClick:ze,readOnly:w,disabled:He,...!E&&{type:it(),inputMode:A.inputMode??at(),min:Fe,max:Ie},...E&&Ve!==void 0&&{rows:Ve}}),je&&d(`div`,{className:l(r.inputFieldSuffix,O?.suffix),style:k?.suffix,children:je})]}),d(`fieldset`,{className:l(r.inputFieldset,O?.fieldset,{[r.
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../tooltip/tooltip.mjs";import ee from"../animatedIcon/animatedIcon.mjs";import r from"./input.module.mjs";import{mergeRefs as te}from"../utils/mergeRefs.mjs";import{InputIconPosition as i,InputType as a}from"./input.types.mjs";import ne,{useCallback as re,useEffect as o,useId as ie,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as u}from"@fortawesome/react-fontawesome";import{faEnvelope as ae,faEye as oe,faEyeSlash as se,faPen as ce,faPhone as le,faSearch as ue,faXmark as de}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";var fe={clear:`Clear`,showPassword:`Show password`,hidePassword:`Hide password`,clearSearch:`Clear search`};function p(e){if(e)return{role:`button`,tabIndex:0,onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),e(t))}}}function pe(pe){let{name:me=``,id:he,label:m=``,errorMessage:h=``,isValid:ge=!0,className:_e=``,style:ve,value:ye=``,icon:g=null,iconPosition:be,onChange:_=()=>{},onFocus:xe=null,onBlur:Se=null,onMouseEnter:Ce=null,onMouseLeave:we=null,isUppercase:Te=!1,isRequired:Ee=!1,tabIndex:De=0,type:v=void 0,isRow:Oe=!1,onIconClick:y=null,parse:ke=null,formValidator:b=null,autoFocus:Ae=!1,suffix:je=null,upperRightLabel:x=null,labelInBorder:Me=!1,showCancel:Ne=!1,onClearClicked:Pe,min:Fe=null,max:Ie=null,placeholder:S=``,useMatLabelStyle:C=!0,alwaysFloatLabel:Le=!1,autoComplete:Re=null,onClick:ze=()=>null,readOnly:w=!1,customInput:T,alwaysShowCancel:Be=!1,multiline:E=!1,rows:Ve,autoResize:D=!1,classNames:O,styles:k,disabled:He=!1,labels:Ue,ref:We,...A}=e(`Input`,pe),j=t(`Input`,Ue,fe),[M,Ge]=c(!1),[Ke,N]=c(!1),[P,F]=c(!1),[I,L]=c(!1),[,qe]=c(0),R=s(null),z=s(b),Je=ie(),Ye=he||me||Je,B=b?b.value:ye,Xe=s(!1),V=re(()=>{let e=R.current;if(e){if(!D){Xe.current&&=(e.style.height=``,!1);return}e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`,Xe.current=!0}},[D]);o(()=>{V()},[V,B]),o(()=>{Ae&&R.current&&setTimeout(()=>{R.current?.focus()},1)},[Ae]),o(()=>{z.current=b;let e=z.current;if(!e)return;e.label=m??``,e.validate();let t=()=>qe(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[b,m]);let Ze=()=>b?b.touched&&b.hasError():!ge||!!h,Qe=()=>!!(Ee||b&&b.validators.find(e=>e.validatorId===`required`)),$e=e=>{F(!0),Ce&&Ce(e)},et=e=>{F(!1),we&&we(e)},tt=e=>{Ge(!0),xe&&xe(e)},nt=e=>{z.current&&(z.current.touched=!0),Ge(!1),Se&&Se(e)},rt=e=>{let t,n;typeof e==`string`?n=e:(t=e,n=e.target.value),ke&&(n=ke(n)),b&&b.set(n),_&&_(n,t),V()},it=()=>{if(v!==a.Email)return v===a.Phone?`tel`:v===a.Search?`search`:v===`password`&&I?`text`:v},at=()=>{if(v===a.Email)return`email`;if(v===a.Phone)return`tel`;if(v===a.Search)return`search`},ot=v&&v!==a.Date&&v!==a.Iban&&v!==a.HasNoIcon,st=v===a.Search||v===a.Password,H=!!g,ct=H&&!st,lt=v===a.Email||v===a.Phone||v===a.Search,U=(be??(!C&<?i.Left:i.Right))===i.Left,W={className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!y,[r.inputFieldIconLeft]:U}),onClick:y??void 0,...p(y)},G=g,K=y??G?.props.onClick,ut=ct&&G?ne.cloneElement(G,{className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!K,[r.inputFieldIconLeft]:U},G.props.className),onClick:K,...p(K)}):null,dt=(ot||H)&&U,ft={className:l(r.inputFieldIcon,r.inputFieldIconPassword,{[r.inputFieldIconLeft]:U}),onClick:()=>L(!I),...p(()=>L(!I)),"aria-label":I?j.hidePassword:j.showPassword},q=Ze(),J=m;J&&Qe()&&(J=`${J} *`);let pt=Qe()?d(`span`,{className:r.requiredMark,children:`*`}):null,Y=M&&!w,X=C&&(Le||Y||B||Ke||v===a.Date),mt=e=>{let{animationName:t}=e;if(t===r.inputAutofillStart){N(!0);let t=e.currentTarget.value;t&&t!==String(B??``)&&rt(t);return}if(t===r.inputAutofillEnd){N(!1);return}A.onAnimationStart?.(e)},Z=v===a.Search&&!!B,Q=T?.props;function $(){b&&b.set(``),_?.(``),Pe?.()}return f(`div`,{className:l(r.input,{[r.inputIconLeft]:dt},_e),style:ve,children:[f(`label`,{htmlFor:Ye,className:l(r.inputLabel,O?.label,{[r.inputLabelColumn]:!Oe,[r.inputLabelInBorder]:Me}),style:k?.label,children:[m&&!C?f(`span`,{className:r.inputLabelText,children:[m,pt]}):null,x&&d(`div`,{className:l(r.inputUpperRightLabel,O?.upperRightLabel),style:k?.upperRightLabel,children:x}),f(`div`,{className:l(r.inputContainer,{[r.inputContainerMultiline]:E},O?.container),style:k?.container,onMouseEnter:$e,onMouseLeave:et,children:[ut,ne.cloneElement(T||d(E?`textarea`:`input`,{}),{...A,className:l(r.inputField,{[r.inputFieldUppercase]:Te,[r.inputFieldDate]:v===a.Date,[r.inputFieldDefault]:v!==a.Date,[r.inputFieldWithIcon]:ot||H,[r.inputFieldWithIconLeft]:dt,[r.inputFieldMultiline]:E,[r.inputFieldMultilineAutoResize]:E&&D,[r.inputFieldReadOnly]:w&&!He},O?.input,Q?.className),style:Q?.style||k?.input?{...Q?.style,...k?.input}:void 0,name:me,id:Ye,value:B,tabIndex:De,ref:T?void 0:te(R,We),onChange:rt,onAnimationStart:mt,onFocus:tt,onBlur:nt,placeholder:C?``:S,autoComplete:Re,onClick:ze,readOnly:w,disabled:He,...!E&&{type:it(),inputMode:A.inputMode??at(),min:Fe,max:Ie},...E&&Ve!==void 0&&{rows:Ve}}),je&&d(`div`,{className:l(r.inputFieldSuffix,O?.suffix),style:k?.suffix,children:je})]}),d(`fieldset`,{className:l(r.inputFieldset,O?.fieldset,{[r.inputContainerBorderFocused]:Y,[r.inputContainerBorderSearchFocused]:v===a.Search&&(Y||!!B),[r.inputContainerBorderInvalid]:q,[r.inputContainerBorderError]:q}),style:k?.fieldset,children:d(`legend`,{className:l(r.inputFieldsetLegend,{[r.inputFieldsetLegendActive]:X}),children:d(`span`,{children:J})})}),C&&m&&f(`div`,{className:l(r.inputMatLabel,{[r.inputMatLabelFloating]:X,[r.inputMatLabelMultiline]:E&&!X}),children:[m,pt]}),C&&S&&d(`div`,{className:l(r.inputPlaceholder,Y&&!B&&!Ke&&r.inputPlaceholderShown,E&&r.inputPlaceholderMultiline),children:S}),d(n,{variant:`error`,message:b?b.getCurrentErrorMessage():h||``,isVisible:q&&(M||P)})]}),v===a.Phone&&!H&&d(u,{icon:le,...W}),v===a.Email&&!H&&d(u,{icon:ae,...W}),v===a.Edit&&!H&&d(u,{icon:ce,...W}),v===a.Search&&d(ee,{icon:Z?de:ue,className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!Z,[r.inputFieldIconLeft]:U}),onClick:Z?$:void 0,...p(Z?$:void 0),"aria-label":Z?j.clearSearch:void 0}),v===a.Password&&d(u,{icon:I?se:oe,...ft}),(Ne&&B&&(M||P)||Be)&&v!==a.Search&&d(u,{icon:de,className:r.inputFieldIcon,onClick:$,onMouseEnter:()=>F(!0),onMouseLeave:()=>F(!1),...p($),"aria-label":j.clear})]})}export{pe as default};
|
|
2
2
|
//# sourceMappingURL=input.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.mjs","names":[],"sources":["../../../../package/common/input/input.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useId } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n faPhone,\n faPen,\n faEnvelope,\n faSearch,\n faXmark,\n faEye as passwordHiddenIcon,\n faEyeSlash as passwordShownIcon,\n} from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\nimport AnimatedIcon from 'components/common/animatedIcon';\nimport { useComponentDefaults, useLabels } from 'components/common/configProvider';\nimport styles from './input.module.pcss';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport type { InputLabels, InputProps } from './input.types';\nimport { InputType, InputIconPosition } from './input.types';\n\nconst DEFAULT_LABELS: Required<InputLabels> = {\n clear: 'Clear',\n showPassword: 'Show password',\n hidePassword: 'Hide password',\n clearSearch: 'Clear search',\n};\n\nfunction keyboardActivationProps(onActivate?: ((event: React.SyntheticEvent) => void) | null) {\n if (!onActivate) return undefined;\n return {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: (event: React.KeyboardEvent) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n onActivate(event);\n },\n };\n}\n\nfunction Input(props: InputProps) {\n const {\n name = '',\n id,\n label = '',\n errorMessage = '',\n isValid = true,\n className = '',\n style,\n value = '',\n icon = null,\n iconPosition,\n onChange = () => {},\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n isUppercase = false,\n isRequired = false,\n tabIndex = 0,\n type = undefined,\n isRow = false,\n onIconClick = null,\n parse = null,\n formValidator = null,\n autoFocus = false,\n suffix = null,\n upperRightLabel = null,\n labelInBorder = false,\n showCancel = false,\n onClearClicked,\n min = null,\n max = null,\n placeholder = '',\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n autoComplete = null,\n onClick = () => null,\n readOnly = false,\n customInput,\n alwaysShowCancel = false,\n multiline = false,\n rows,\n autoResize = false,\n classNames,\n styles: slotStyles,\n disabled = false,\n labels,\n ref,\n ...rest\n } = useComponentDefaults('Input', props);\n const text = useLabels('Input', labels, DEFAULT_LABELS);\n const [isFocused, setIsFocused] = useState(false);\n // Chrome fills a field on page load without firing anything React sees. The animation the\n // stylesheet binds to :-webkit-autofill is the only notification it gives.\n const [isAutofilled, setIsAutofilled] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [isPasswordShown, setIsPasswordShown] = useState(false);\n const [, forceUpdate] = useState(0);\n\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const fvRef = useRef(formValidator);\n\n const generatedId = useId();\n const inputId = id || name || generatedId;\n\n const currVal = formValidator ? formValidator.value : value;\n const didSetAutoHeightRef = useRef(false);\n\n const resizeTextarea = useCallback(() => {\n const el = inputRef.current;\n if (!el) return;\n if (!autoResize) {\n // Only undo a height this set, so an explicit one from `styles.input` survives.\n if (didSetAutoHeightRef.current) {\n el.style.height = '';\n didSetAutoHeightRef.current = false;\n }\n return;\n }\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n didSetAutoHeightRef.current = true;\n }, [autoResize]);\n\n // `currVal` is a dependency because the value can change without passing through\n // `onChangeHandler`: the clear button, a parent resetting it, or `formValidator.set` elsewhere.\n useEffect(() => {\n resizeTextarea();\n }, [resizeTextarea, currVal]);\n\n useEffect(() => {\n if (autoFocus && inputRef.current) {\n setTimeout(() => {\n inputRef.current?.focus();\n }, 1);\n }\n }, [autoFocus]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label ?? '';\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired ||\n (formValidator &&\n formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n );\n };\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n if (onMouseEnterProp) onMouseEnterProp(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n if (onMouseLeaveProp) onMouseLeaveProp(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n if (onFocusProp) onFocusProp(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n if (onBlurProp) onBlurProp(event);\n };\n\n const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void => {\n let parsedEvent: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined;\n let val: string;\n\n if (typeof event !== 'string') {\n parsedEvent = event;\n val = event.target.value;\n } else {\n val = event;\n }\n\n if (parse) val = parse(val);\n if (formValidator) formValidator.set(val);\n if (onChange) onChange(val, parsedEvent);\n resizeTextarea();\n };\n\n const getInputType = (): string | undefined => {\n if (type === InputType.Email) return undefined;\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n if (type === 'password' && isPasswordShown) return 'text';\n return type;\n };\n\n const getInputMode = (): React.HTMLAttributes<HTMLInputElement>['inputMode'] => {\n if (type === InputType.Email) return 'email';\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n return undefined;\n };\n\n const hasIcon = type && type !== InputType.Date && type !== InputType.Iban && type !== InputType.HasNoIcon;\n\n const isFunctionalIconType = type === InputType.Search || type === InputType.Password;\n const hasCustomIcon = !!icon;\n const customIconApplies = hasCustomIcon && !isFunctionalIconType;\n\n const defaultsLeftWithoutMatLabel = type === InputType.Email || type === InputType.Phone || type === InputType.Search;\n const resolvedIconPosition =\n iconPosition ??\n (!useMatLabelStyle && defaultsLeftWithoutMatLabel ? InputIconPosition.Left : InputIconPosition.Right);\n const iconIsLeft = resolvedIconPosition === InputIconPosition.Left;\n\n const iconProps = {\n className: cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !onIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n }),\n onClick: onIconClick ?? undefined,\n ...keyboardActivationProps(onIconClick),\n };\n\n const customIcon = icon as React.ReactElement<{ className?: string; onClick?: React.MouseEventHandler }> | null;\n const resolvedCustomIconClick = onIconClick ?? customIcon?.props.onClick;\n const renderedIcon =\n customIconApplies && customIcon\n ? React.cloneElement(customIcon, {\n className: cx(\n styles.inputFieldIcon,\n {\n [styles.inputFieldIconNotClickable]: !resolvedCustomIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n },\n customIcon.props.className,\n ),\n onClick: resolvedCustomIconClick,\n ...keyboardActivationProps(resolvedCustomIconClick as ((event: React.SyntheticEvent) => void) | undefined),\n })\n : null;\n\n const hasLeftIcon = (hasIcon || hasCustomIcon) && iconIsLeft;\n\n const passwordIconProps = {\n className: cx(styles.inputFieldIcon, styles.inputFieldIconPassword, { [styles.inputFieldIconLeft]: iconIsLeft }),\n onClick: () => setIsPasswordShown(!isPasswordShown),\n ...keyboardActivationProps(() => setIsPasswordShown(!isPasswordShown)),\n 'aria-label': isPasswordShown ? text.hidePassword : text.showPassword,\n };\n\n const hasErr = hasError();\n\n let editedLabel = label;\n if (editedLabel && computedIsRequired()) editedLabel = `${editedLabel} *`;\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n\n const showFocus = isFocused && !readOnly;\n const shouldFloatLabel =\n useMatLabelStyle && (alwaysFloatLabel || showFocus || currVal || isAutofilled || type === InputType.Date);\n\n const handleAutofillAnimation = (event: React.AnimationEvent) => {\n const { animationName } = event;\n if (animationName === styles.inputAutofillStart) {\n setIsAutofilled(true);\n // The field is controlled, so the commit this triggers would write `value` back over\n // what the browser just put there and blank it. Replay it as an ordinary change first.\n const filled = (event.currentTarget as HTMLInputElement).value;\n if (filled && filled !== String(currVal ?? '')) onChangeHandler(filled);\n return;\n }\n if (animationName === styles.inputAutofillEnd) {\n setIsAutofilled(false);\n return;\n }\n // Not ours. `onAnimationStart` arrives through `rest`, and the consumer must not receive\n // the two above: the `:not()` rule fires one of them on every mount.\n rest.onAnimationStart?.(event as React.AnimationEvent<HTMLInputElement>);\n };\n\n const isSearchCleared = type === InputType.Search && !!currVal;\n\n const customInputProps = customInput?.props as { className?: string; style?: React.CSSProperties } | undefined;\n\n function handleClear() {\n if (formValidator) formValidator.set('');\n onChange?.('');\n onClearClicked?.();\n }\n\n return (\n <div className={cx(styles.input, { [styles.inputIconLeft]: hasLeftIcon }, className)} style={style}>\n <label\n htmlFor={inputId}\n className={cx(styles.inputLabel, classNames?.label, {\n [styles.inputLabelColumn]: !isRow,\n [styles.inputLabelInBorder]: labelInBorder,\n })}\n style={slotStyles?.label}\n >\n {label && !useMatLabelStyle ? (\n <span className={styles.inputLabelText}>\n {label}\n {requiredMark}\n </span>\n ) : null}\n {upperRightLabel && (\n <div\n className={cx(styles.inputUpperRightLabel, classNames?.upperRightLabel)}\n style={slotStyles?.upperRightLabel}\n >\n {upperRightLabel}\n </div>\n )}\n <div\n className={cx(styles.inputContainer, { [styles.inputContainerMultiline]: multiline }, classNames?.container)}\n style={slotStyles?.container}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n {renderedIcon}\n {React.cloneElement(customInput || (multiline ? <textarea /> : <input />), {\n ...rest,\n className: cx(\n styles.inputField,\n {\n [styles.inputFieldUppercase]: isUppercase,\n [styles.inputFieldDate]: type === InputType.Date,\n [styles.inputFieldDefault]: type !== InputType.Date,\n [styles.inputFieldWithIcon]: hasIcon || hasCustomIcon,\n [styles.inputFieldWithIconLeft]: hasLeftIcon,\n [styles.inputFieldMultiline]: multiline,\n [styles.inputFieldMultilineAutoResize]: multiline && autoResize,\n [styles.inputFieldReadOnly]: readOnly && !disabled,\n },\n classNames?.input,\n customInputProps?.className,\n ),\n style:\n customInputProps?.style || slotStyles?.input\n ? { ...customInputProps?.style, ...slotStyles?.input }\n : undefined,\n name,\n id: inputId,\n value: currVal,\n tabIndex,\n ref: customInput ? undefined : mergeRefs(inputRef, ref),\n onChange: onChangeHandler,\n onAnimationStart: handleAutofillAnimation,\n onFocus: onFocusHandler,\n onBlur: onBlurHandler,\n placeholder: !useMatLabelStyle ? placeholder : '',\n autoComplete,\n onClick,\n readOnly,\n disabled,\n ...(!multiline && { type: getInputType(), inputMode: rest.inputMode ?? getInputMode(), min, max }),\n ...(multiline && rows !== undefined && { rows }),\n })}\n {suffix && (\n <div className={cx(styles.inputFieldSuffix, classNames?.suffix)} style={slotStyles?.suffix}>\n {suffix}\n </div>\n )}\n </div>\n <fieldset\n className={cx(styles.inputFieldset, classNames?.fieldset, {\n [styles.inputFieldsetNoNotch]: !label,\n [styles.inputContainerBorderFocused]: showFocus,\n [styles.inputContainerBorderSearchFocused]: type === InputType.Search && (showFocus || !!currVal),\n [styles.inputContainerBorderInvalid]: hasErr,\n [styles.inputContainerBorderError]: hasErr,\n })}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(styles.inputFieldsetLegend, {\n [styles.inputFieldsetLegendActive]: shouldFloatLabel,\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(styles.inputMatLabel, {\n [styles.inputMatLabelFloating]: shouldFloatLabel,\n [styles.inputMatLabelMultiline]: multiline && !shouldFloatLabel,\n })}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.inputPlaceholder,\n showFocus && !currVal && !isAutofilled && styles.inputPlaceholderShown,\n multiline && styles.inputPlaceholderMultiline,\n )}\n >\n {placeholder}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </label>\n\n {type === InputType.Phone && !hasCustomIcon && <FontAwesomeIcon icon={faPhone} {...iconProps} />}\n {type === InputType.Email && !hasCustomIcon && <FontAwesomeIcon icon={faEnvelope} {...iconProps} />}\n {type === InputType.Edit && !hasCustomIcon && <FontAwesomeIcon icon={faPen} {...iconProps} />}\n {type === InputType.Search && (\n <AnimatedIcon\n icon={isSearchCleared ? faXmark : faSearch}\n className={cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !isSearchCleared,\n [styles.inputFieldIconLeft]: iconIsLeft,\n })}\n onClick={isSearchCleared ? handleClear : undefined}\n {...keyboardActivationProps(isSearchCleared ? handleClear : undefined)}\n aria-label={isSearchCleared ? text.clearSearch : undefined}\n />\n )}\n {type === InputType.Password && (\n <FontAwesomeIcon icon={isPasswordShown ? passwordShownIcon : passwordHiddenIcon} {...passwordIconProps} />\n )}\n\n {((showCancel && currVal && (isFocused || isHovered)) || alwaysShowCancel) && type !== InputType.Search && (\n <FontAwesomeIcon\n icon={faXmark}\n className={styles.inputFieldIcon}\n onClick={handleClear}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...keyboardActivationProps(handleClear)}\n aria-label={text.clear}\n />\n )}\n </div>\n );\n}\n\nexport default Input;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input.mjs","names":[],"sources":["../../../../package/common/input/input.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useId } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n faPhone,\n faPen,\n faEnvelope,\n faSearch,\n faXmark,\n faEye as passwordHiddenIcon,\n faEyeSlash as passwordShownIcon,\n} from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\nimport AnimatedIcon from 'components/common/animatedIcon';\nimport { useComponentDefaults, useLabels } from 'components/common/configProvider';\nimport styles from './input.module.pcss';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport type { InputLabels, InputProps } from './input.types';\nimport { InputType, InputIconPosition } from './input.types';\n\nconst DEFAULT_LABELS: Required<InputLabels> = {\n clear: 'Clear',\n showPassword: 'Show password',\n hidePassword: 'Hide password',\n clearSearch: 'Clear search',\n};\n\nfunction keyboardActivationProps(onActivate?: ((event: React.SyntheticEvent) => void) | null) {\n if (!onActivate) return undefined;\n return {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: (event: React.KeyboardEvent) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n onActivate(event);\n },\n };\n}\n\nfunction Input(props: InputProps) {\n const {\n name = '',\n id,\n label = '',\n errorMessage = '',\n isValid = true,\n className = '',\n style,\n value = '',\n icon = null,\n iconPosition,\n onChange = () => {},\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n isUppercase = false,\n isRequired = false,\n tabIndex = 0,\n type = undefined,\n isRow = false,\n onIconClick = null,\n parse = null,\n formValidator = null,\n autoFocus = false,\n suffix = null,\n upperRightLabel = null,\n labelInBorder = false,\n showCancel = false,\n onClearClicked,\n min = null,\n max = null,\n placeholder = '',\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n autoComplete = null,\n onClick = () => null,\n readOnly = false,\n customInput,\n alwaysShowCancel = false,\n multiline = false,\n rows,\n autoResize = false,\n classNames,\n styles: slotStyles,\n disabled = false,\n labels,\n ref,\n ...rest\n } = useComponentDefaults('Input', props);\n const text = useLabels('Input', labels, DEFAULT_LABELS);\n const [isFocused, setIsFocused] = useState(false);\n // Chrome fills a field on page load without firing anything React sees. The animation the\n // stylesheet binds to :-webkit-autofill is the only notification it gives.\n const [isAutofilled, setIsAutofilled] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [isPasswordShown, setIsPasswordShown] = useState(false);\n const [, forceUpdate] = useState(0);\n\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const fvRef = useRef(formValidator);\n\n const generatedId = useId();\n const inputId = id || name || generatedId;\n\n const currVal = formValidator ? formValidator.value : value;\n const didSetAutoHeightRef = useRef(false);\n\n const resizeTextarea = useCallback(() => {\n const el = inputRef.current;\n if (!el) return;\n if (!autoResize) {\n // Only undo a height this set, so an explicit one from `styles.input` survives.\n if (didSetAutoHeightRef.current) {\n el.style.height = '';\n didSetAutoHeightRef.current = false;\n }\n return;\n }\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n didSetAutoHeightRef.current = true;\n }, [autoResize]);\n\n // `currVal` is a dependency because the value can change without passing through\n // `onChangeHandler`: the clear button, a parent resetting it, or `formValidator.set` elsewhere.\n useEffect(() => {\n resizeTextarea();\n }, [resizeTextarea, currVal]);\n\n useEffect(() => {\n if (autoFocus && inputRef.current) {\n setTimeout(() => {\n inputRef.current?.focus();\n }, 1);\n }\n }, [autoFocus]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label ?? '';\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired ||\n (formValidator &&\n formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n );\n };\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n if (onMouseEnterProp) onMouseEnterProp(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n if (onMouseLeaveProp) onMouseLeaveProp(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n if (onFocusProp) onFocusProp(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n if (onBlurProp) onBlurProp(event);\n };\n\n const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void => {\n let parsedEvent: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined;\n let val: string;\n\n if (typeof event !== 'string') {\n parsedEvent = event;\n val = event.target.value;\n } else {\n val = event;\n }\n\n if (parse) val = parse(val);\n if (formValidator) formValidator.set(val);\n if (onChange) onChange(val, parsedEvent);\n resizeTextarea();\n };\n\n const getInputType = (): string | undefined => {\n if (type === InputType.Email) return undefined;\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n if (type === 'password' && isPasswordShown) return 'text';\n return type;\n };\n\n const getInputMode = (): React.HTMLAttributes<HTMLInputElement>['inputMode'] => {\n if (type === InputType.Email) return 'email';\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n return undefined;\n };\n\n const hasIcon = type && type !== InputType.Date && type !== InputType.Iban && type !== InputType.HasNoIcon;\n\n const isFunctionalIconType = type === InputType.Search || type === InputType.Password;\n const hasCustomIcon = !!icon;\n const customIconApplies = hasCustomIcon && !isFunctionalIconType;\n\n const defaultsLeftWithoutMatLabel = type === InputType.Email || type === InputType.Phone || type === InputType.Search;\n const resolvedIconPosition =\n iconPosition ??\n (!useMatLabelStyle && defaultsLeftWithoutMatLabel ? InputIconPosition.Left : InputIconPosition.Right);\n const iconIsLeft = resolvedIconPosition === InputIconPosition.Left;\n\n const iconProps = {\n className: cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !onIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n }),\n onClick: onIconClick ?? undefined,\n ...keyboardActivationProps(onIconClick),\n };\n\n const customIcon = icon as React.ReactElement<{ className?: string; onClick?: React.MouseEventHandler }> | null;\n const resolvedCustomIconClick = onIconClick ?? customIcon?.props.onClick;\n const renderedIcon =\n customIconApplies && customIcon\n ? React.cloneElement(customIcon, {\n className: cx(\n styles.inputFieldIcon,\n {\n [styles.inputFieldIconNotClickable]: !resolvedCustomIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n },\n customIcon.props.className,\n ),\n onClick: resolvedCustomIconClick,\n ...keyboardActivationProps(resolvedCustomIconClick as ((event: React.SyntheticEvent) => void) | undefined),\n })\n : null;\n\n const hasLeftIcon = (hasIcon || hasCustomIcon) && iconIsLeft;\n\n const passwordIconProps = {\n className: cx(styles.inputFieldIcon, styles.inputFieldIconPassword, { [styles.inputFieldIconLeft]: iconIsLeft }),\n onClick: () => setIsPasswordShown(!isPasswordShown),\n ...keyboardActivationProps(() => setIsPasswordShown(!isPasswordShown)),\n 'aria-label': isPasswordShown ? text.hidePassword : text.showPassword,\n };\n\n const hasErr = hasError();\n\n let editedLabel = label;\n if (editedLabel && computedIsRequired()) editedLabel = `${editedLabel} *`;\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n\n const showFocus = isFocused && !readOnly;\n const shouldFloatLabel =\n useMatLabelStyle && (alwaysFloatLabel || showFocus || currVal || isAutofilled || type === InputType.Date);\n\n const handleAutofillAnimation = (event: React.AnimationEvent) => {\n const { animationName } = event;\n if (animationName === styles.inputAutofillStart) {\n setIsAutofilled(true);\n // The field is controlled, so the commit this triggers would write `value` back over\n // what the browser just put there and blank it. Replay it as an ordinary change first.\n const filled = (event.currentTarget as HTMLInputElement).value;\n if (filled && filled !== String(currVal ?? '')) onChangeHandler(filled);\n return;\n }\n if (animationName === styles.inputAutofillEnd) {\n setIsAutofilled(false);\n return;\n }\n // Not ours. `onAnimationStart` arrives through `rest`, and the consumer must not receive\n // the two above: the `:not()` rule fires one of them on every mount.\n rest.onAnimationStart?.(event as React.AnimationEvent<HTMLInputElement>);\n };\n\n const isSearchCleared = type === InputType.Search && !!currVal;\n\n const customInputProps = customInput?.props as { className?: string; style?: React.CSSProperties } | undefined;\n\n function handleClear() {\n if (formValidator) formValidator.set('');\n onChange?.('');\n onClearClicked?.();\n }\n\n return (\n <div className={cx(styles.input, { [styles.inputIconLeft]: hasLeftIcon }, className)} style={style}>\n <label\n htmlFor={inputId}\n className={cx(styles.inputLabel, classNames?.label, {\n [styles.inputLabelColumn]: !isRow,\n [styles.inputLabelInBorder]: labelInBorder,\n })}\n style={slotStyles?.label}\n >\n {label && !useMatLabelStyle ? (\n <span className={styles.inputLabelText}>\n {label}\n {requiredMark}\n </span>\n ) : null}\n {upperRightLabel && (\n <div\n className={cx(styles.inputUpperRightLabel, classNames?.upperRightLabel)}\n style={slotStyles?.upperRightLabel}\n >\n {upperRightLabel}\n </div>\n )}\n <div\n className={cx(styles.inputContainer, { [styles.inputContainerMultiline]: multiline }, classNames?.container)}\n style={slotStyles?.container}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n {renderedIcon}\n {React.cloneElement(customInput || (multiline ? <textarea /> : <input />), {\n ...rest,\n className: cx(\n styles.inputField,\n {\n [styles.inputFieldUppercase]: isUppercase,\n [styles.inputFieldDate]: type === InputType.Date,\n [styles.inputFieldDefault]: type !== InputType.Date,\n [styles.inputFieldWithIcon]: hasIcon || hasCustomIcon,\n [styles.inputFieldWithIconLeft]: hasLeftIcon,\n [styles.inputFieldMultiline]: multiline,\n [styles.inputFieldMultilineAutoResize]: multiline && autoResize,\n [styles.inputFieldReadOnly]: readOnly && !disabled,\n },\n classNames?.input,\n customInputProps?.className,\n ),\n style:\n customInputProps?.style || slotStyles?.input\n ? { ...customInputProps?.style, ...slotStyles?.input }\n : undefined,\n name,\n id: inputId,\n value: currVal,\n tabIndex,\n ref: customInput ? undefined : mergeRefs(inputRef, ref),\n onChange: onChangeHandler,\n onAnimationStart: handleAutofillAnimation,\n onFocus: onFocusHandler,\n onBlur: onBlurHandler,\n placeholder: !useMatLabelStyle ? placeholder : '',\n autoComplete,\n onClick,\n readOnly,\n disabled,\n ...(!multiline && { type: getInputType(), inputMode: rest.inputMode ?? getInputMode(), min, max }),\n ...(multiline && rows !== undefined && { rows }),\n })}\n {suffix && (\n <div className={cx(styles.inputFieldSuffix, classNames?.suffix)} style={slotStyles?.suffix}>\n {suffix}\n </div>\n )}\n </div>\n <fieldset\n className={cx(styles.inputFieldset, classNames?.fieldset, {\n [styles.inputContainerBorderFocused]: showFocus,\n [styles.inputContainerBorderSearchFocused]: type === InputType.Search && (showFocus || !!currVal),\n [styles.inputContainerBorderInvalid]: hasErr,\n [styles.inputContainerBorderError]: hasErr,\n })}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(styles.inputFieldsetLegend, {\n [styles.inputFieldsetLegendActive]: shouldFloatLabel,\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(styles.inputMatLabel, {\n [styles.inputMatLabelFloating]: shouldFloatLabel,\n [styles.inputMatLabelMultiline]: multiline && !shouldFloatLabel,\n })}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.inputPlaceholder,\n showFocus && !currVal && !isAutofilled && styles.inputPlaceholderShown,\n multiline && styles.inputPlaceholderMultiline,\n )}\n >\n {placeholder}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </label>\n\n {type === InputType.Phone && !hasCustomIcon && <FontAwesomeIcon icon={faPhone} {...iconProps} />}\n {type === InputType.Email && !hasCustomIcon && <FontAwesomeIcon icon={faEnvelope} {...iconProps} />}\n {type === InputType.Edit && !hasCustomIcon && <FontAwesomeIcon icon={faPen} {...iconProps} />}\n {type === InputType.Search && (\n <AnimatedIcon\n icon={isSearchCleared ? faXmark : faSearch}\n className={cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !isSearchCleared,\n [styles.inputFieldIconLeft]: iconIsLeft,\n })}\n onClick={isSearchCleared ? handleClear : undefined}\n {...keyboardActivationProps(isSearchCleared ? handleClear : undefined)}\n aria-label={isSearchCleared ? text.clearSearch : undefined}\n />\n )}\n {type === InputType.Password && (\n <FontAwesomeIcon icon={isPasswordShown ? passwordShownIcon : passwordHiddenIcon} {...passwordIconProps} />\n )}\n\n {((showCancel && currVal && (isFocused || isHovered)) || alwaysShowCancel) && type !== InputType.Search && (\n <FontAwesomeIcon\n icon={faXmark}\n className={styles.inputFieldIcon}\n onClick={handleClear}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...keyboardActivationProps(handleClear)}\n aria-label={text.clear}\n />\n )}\n </div>\n );\n}\n\nexport default Input;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";var e=`required-mark_xGJdX`,t=`input_VKNCE`,n=`input-placeholder_IZH-l`,r=`input-placeholder-shown_mm35e`,i=`fadeIn_kmX3i`,a=`input-placeholder-multiline_tLHAl`,o=`input-matLabel_-05ey`,s=`input-matLabel-floating_1P2YE`,c=`input-matLabel-multiline_miIZJ`,l=`input-fieldset_XCibg`,u=`input-fieldset-
|
|
1
|
+
"use client";var e=`required-mark_xGJdX`,t=`input_VKNCE`,n=`input-placeholder_IZH-l`,r=`input-placeholder-shown_mm35e`,i=`fadeIn_kmX3i`,a=`input-placeholder-multiline_tLHAl`,o=`input-matLabel_-05ey`,s=`input-matLabel-floating_1P2YE`,c=`input-matLabel-multiline_miIZJ`,l=`input-fieldset_XCibg`,u=`input-fieldset-legend_X23zp`,d=`input-fieldset-legend-active_t-9hu`,f=`input-iconLeft_itIuo`,p=`input-container_M1RoG`,m=`input-container-multiline_eLw7h`,h=`input-container-border_l131I`,g=`input-container-border-focused_ct6oY`,_=`input-container-border-searchFocused_lq088`,v=`input-container-border-invalid_Wdbq2`,y=`input-container-border-error_gzfPG`,b=`input-label_w1hMR`,x=`input-label-column_qBFPC`,S=`input-label-text_-J5qQ`,C=`input-upper-right-label_I-WG3`,w=`input-field_pmDvZ`,T=`inputAutofillStart_tOifY`,E=`inputAutofillEnd_KLDQQ`,D=`input-field-withIcon_VpfmK`,O=`input-field-withIcon-left_Io7lU`,k=`input-field-suffix_NZpXp`,A=`input-field-date_UHJEZ`,j=`input-field-default_TKipA`,M=`input-field-readOnly_Eut34`,N=`input-field-uppercase_UV6f1`,P=`input-field-multiline_al3V1`,F=`input-field-multiline-autoResize_nK33P`,I=`input-field-icon_YyJIr`,L=`input-field-icon-left_m2XB1`,R=`input-field-icon-notClickable_7j24I`,z=`input-error_LIG23`,B={"required-mark":`required-mark_xGJdX`,requiredMark:e,input:t,"input-placeholder":`input-placeholder_IZH-l`,inputPlaceholder:n,"input-placeholder-shown":`input-placeholder-shown_mm35e`,inputPlaceholderShown:r,fadeIn:i,"input-placeholder-multiline":`input-placeholder-multiline_tLHAl`,inputPlaceholderMultiline:a,"input-matLabel":`input-matLabel_-05ey`,inputMatLabel:o,"input-matLabel-floating":`input-matLabel-floating_1P2YE`,inputMatLabelFloating:s,"input-matLabel-multiline":`input-matLabel-multiline_miIZJ`,inputMatLabelMultiline:c,"input-fieldset":`input-fieldset_XCibg`,inputFieldset:l,"input-fieldset-legend":`input-fieldset-legend_X23zp`,inputFieldsetLegend:u,"input-fieldset-legend-active":`input-fieldset-legend-active_t-9hu`,inputFieldsetLegendActive:d,"input-iconLeft":`input-iconLeft_itIuo`,inputIconLeft:f,"input-container":`input-container_M1RoG`,inputContainer:p,"input-container-multiline":`input-container-multiline_eLw7h`,inputContainerMultiline:m,"input-container-border":`input-container-border_l131I`,inputContainerBorder:h,"input-container-border-focused":`input-container-border-focused_ct6oY`,inputContainerBorderFocused:g,"input-container-border-searchFocused":`input-container-border-searchFocused_lq088`,inputContainerBorderSearchFocused:_,"input-container-border-invalid":`input-container-border-invalid_Wdbq2`,inputContainerBorderInvalid:v,"input-container-border-error":`input-container-border-error_gzfPG`,inputContainerBorderError:y,"input-label":`input-label_w1hMR`,inputLabel:b,"input-label-column":`input-label-column_qBFPC`,inputLabelColumn:x,"input-label-text":`input-label-text_-J5qQ`,inputLabelText:S,"input-upper-right-label":`input-upper-right-label_I-WG3`,inputUpperRightLabel:C,"input-field":`input-field_pmDvZ`,inputField:w,inputAutofillStart:T,inputAutofillEnd:E,"input-field-withIcon":`input-field-withIcon_VpfmK`,inputFieldWithIcon:D,"input-field-withIcon-left":`input-field-withIcon-left_Io7lU`,inputFieldWithIconLeft:O,"input-field-suffix":`input-field-suffix_NZpXp`,inputFieldSuffix:k,"input-field-date":`input-field-date_UHJEZ`,inputFieldDate:A,"input-field-default":`input-field-default_TKipA`,inputFieldDefault:j,"input-field-readOnly":`input-field-readOnly_Eut34`,inputFieldReadOnly:M,"input-field-uppercase":`input-field-uppercase_UV6f1`,inputFieldUppercase:N,"input-field-multiline":`input-field-multiline_al3V1`,inputFieldMultiline:P,"input-field-multiline-autoResize":`input-field-multiline-autoResize_nK33P`,inputFieldMultilineAutoResize:F,"input-field-icon":`input-field-icon_YyJIr`,inputFieldIcon:I,"input-field-icon-left":`input-field-icon-left_m2XB1`,inputFieldIconLeft:L,"input-field-icon-notClickable":`input-field-icon-notClickable_7j24I`,inputFieldIconNotClickable:R,"input-error":`input-error_LIG23`,inputError:z};export{B as default,i as fadeIn,t as input,E as inputAutofillEnd,T as inputAutofillStart,p as inputContainer,h as inputContainerBorder,y as inputContainerBorderError,g as inputContainerBorderFocused,v as inputContainerBorderInvalid,_ as inputContainerBorderSearchFocused,m as inputContainerMultiline,z as inputError,w as inputField,A as inputFieldDate,j as inputFieldDefault,I as inputFieldIcon,L as inputFieldIconLeft,R as inputFieldIconNotClickable,P as inputFieldMultiline,F as inputFieldMultilineAutoResize,M as inputFieldReadOnly,k as inputFieldSuffix,N as inputFieldUppercase,D as inputFieldWithIcon,O as inputFieldWithIconLeft,l as inputFieldset,u as inputFieldsetLegend,d as inputFieldsetLegendActive,f as inputIconLeft,b as inputLabel,x as inputLabelColumn,S as inputLabelText,o as inputMatLabel,s as inputMatLabelFloating,c as inputMatLabelMultiline,n as inputPlaceholder,a as inputPlaceholderMultiline,r as inputPlaceholderShown,C as inputUpperRightLabel,e as requiredMark};
|
|
2
2
|
//# sourceMappingURL=input.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.module.mjs","names":[],"sources":["../../../../package/common/input/input.module.pcss"],"sourcesContent":["/* Chrome fills a field on page load without firing anything React can see, and an animation\n bound to the pseudo-class is the only hook it offers. The declarations are inert on purpose;\n the pair exists to be listened for — see handleAutofillAnimation in input.tsx. */\n@keyframes inputAutofillStart {\n from {\n opacity: 1;\n }\n}\n\n@keyframes inputAutofillEnd {\n from {\n opacity: 1;\n }\n}\n\n.required-mark {\n padding: 0 0 0 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.input {\n position: relative;\n width: 100%;\n\n --input-notch-height: 1.75em;\n --input-icon-size: 1.2em;\n --input-icon-offset: 0.6em;\n --input-icon-gutter: 2.2em;\n\n /* The visible border lives on `fieldset`/`container` below, not this root — buried inside a\n `<label>`, out of reach of a plain ancestor selector. --group-radius-* are custom properties\n (not defined here) that a wrapping ButtonGroup sets per corner on the position it occupies;\n unlike border-radius itself they inherit through any nesting depth, so they reach these\n descendants and flatten the right corners when this Input sits inside a ButtonGroup. */\n --input-radius: var(--group-radius-tl, var(--default-border-radius))\n var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n var(--group-radius-bl, var(--default-border-radius));\n\n &-placeholder {\n position: absolute;\n top: 50%;\n left: 7px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n user-select: none;\n pointer-events: none;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n inset: 0 0 auto 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n padding: 0;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--input-radius);\n font-size: 0.8em;\n inset: calc(var(--input-notch-height) / -2) 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-no-notch {\n /* Only Firefox actually renders the reserved notch space as a visible overlap on\n label-less inputs (e.g. crossing a divider directly above it in a tightly-packed\n layout like the sidebar search box); Chrome's rendering was never broken, just\n consistently sized with every other input. @supports (-moz-appearance: none) is a\n reliable Firefox-only feature query, so this scopes the fix to where it's needed\n instead of changing every browser's sizing. */\n @supports (-moz-appearance: none) {\n inset: 0;\n }\n }\n\n &-legend {\n display: block;\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--input-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0.000001px;\n }\n }\n }\n\n &-iconLeft {\n .input-matLabel {\n left: var(--input-icon-gutter);\n\n &-floating {\n left: calc(var(--input-icon-gutter) + 0.5em);\n }\n }\n\n .input-placeholder {\n left: var(--input-icon-gutter);\n }\n\n .input-fieldset {\n padding-left: var(--input-icon-gutter);\n }\n }\n\n &-container {\n display: flex;\n align-items: center;\n border-radius: var(--input-radius);\n background-color: var(--input-background);\n transition: border-color;\n cursor: text;\n height: 100%;\n\n &-multiline {\n overflow: hidden;\n padding-top: 10px;\n align-items: flex-start;\n }\n\n &-border {\n transition: border-color;\n background-color: var(--input-background);\n outline: none;\n\n &-focused {\n outline-color: var(--focus-border-color, var(--primary-color));\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-searchFocused {\n outline-color: var(--search-input-color);\n outline-width: 2px;\n border-color: var(--search-input-color);\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n }\n\n &-label {\n display: flex;\n color: var(--text-color);\n height: 100%;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n margin-right: 5px;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n color: color(var(--border-color) lightness(40%));\n background-color: transparent;\n font-size: 12px;\n position: absolute;\n top: -15px;\n white-space: nowrap;\n }\n }\n\n &-upper-right-label {\n position: absolute;\n transform: translateY(calc(-100% - 1.5px));\n right: 8px;\n min-height: 12px;\n font-size: 12px;\n color: color(var(--border-color) lightness(40%));\n }\n\n &-field {\n appearance: none;\n -webkit-appearance: none;\n font-family: inherit;\n font-size: inherit;\n box-sizing: border-box;\n resize: none;\n width: 100%;\n border: none;\n border-radius: var(--default-border-radius);\n overflow: hidden;\n line-height: 1.25;\n color: var(--text-color);\n background: transparent;\n\n &:focus {\n outline: none;\n }\n\n &::placeholder {\n color: var(--text-light);\n }\n\n /* Chrome overrides `background` and `color` on an autofilled field outright. Clipping the\n background to the glyphs is what removes it: the page cannot win on colour, but it can\n say where a background paints, and -webkit-text-fill-color then beats the text colour.\n The field is transparent either way, so what shows through is whatever is really behind\n it — no surface has to declare itself, which an inset-shadow cover-up would have needed. */\n &:-webkit-autofill,\n &:-webkit-autofill:hover,\n &:-webkit-autofill:focus,\n &:-webkit-autofill:active {\n animation-name: inputAutofillStart;\n animation-duration: 1ms;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: var(--text-color);\n caret-color: var(--text-color);\n }\n\n &:not(:-webkit-autofill) {\n animation-name: inputAutofillEnd;\n animation-duration: 1ms;\n }\n\n /* The browser's own search-cancel decoration doesn't match the theme — Search has its own\n themed clear icon (see isSearchCleared in input.tsx) that replaces it. */\n &::-webkit-search-cancel-button,\n &::-webkit-search-decoration {\n -webkit-appearance: none;\n appearance: none;\n display: none;\n }\n\n &-withIcon {\n padding-right: var(--input-icon-gutter) !important;\n\n &-left {\n padding-right: 0.643em !important;\n padding-left: var(--input-icon-gutter) !important;\n }\n }\n\n &-suffix {\n padding-right: 9px;\n }\n\n &-date {\n padding: 6px 9px;\n color-scheme: dark;\n\n svg {\n color: var(--input-icon-color);\n }\n }\n\n &-default {\n padding: 8px 7.5px 6px;\n }\n\n &[disabled] {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n opacity: 1;\n\n &::placeholder {\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n }\n }\n\n &-readOnly {\n cursor: default;\n color: var(--text-dark);\n -webkit-text-fill-color: var(--text-dark);\n }\n\n &-uppercase {\n text-transform: uppercase;\n }\n\n &-multiline {\n overflow-y: auto;\n resize: vertical;\n padding-top: 4px;\n min-height: 30px;\n\n &-autoResize {\n resize: none;\n overflow: hidden;\n }\n }\n\n &-icon {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: transparent;\n border: none;\n position: absolute;\n outline: none;\n right: var(--input-icon-offset);\n color: var(--input-icon-color);\n width: var(--input-icon-size) !important;\n height: var(--input-icon-size) !important;\n user-select: none;\n top: 50%;\n transform: translateY(-50%);\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n &-left {\n left: var(--input-icon-offset);\n }\n\n &-notClickable {\n pointer-events: none;\n }\n }\n }\n\n &-error {\n height: 1em;\n padding-top: 3px;\n color: var(--error-font-color);\n font-size: 11px;\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 0.5;\n }\n }\n}\n\n/* The label's glide between resting and floating is the motion; it still moves, just instantly. */\n@media (prefers-reduced-motion: reduce) {\n .input-matLabel,\n .input-placeholder {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"input.module.mjs","names":[],"sources":["../../../../package/common/input/input.module.pcss"],"sourcesContent":["/* Chrome fills a field on page load without firing anything React can see, and an animation\n bound to the pseudo-class is the only hook it offers. The declarations are inert on purpose;\n the pair exists to be listened for — see handleAutofillAnimation in input.tsx. */\n@keyframes inputAutofillStart {\n from {\n opacity: 1;\n }\n}\n\n@keyframes inputAutofillEnd {\n from {\n opacity: 1;\n }\n}\n\n.required-mark {\n padding: 0 0 0 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.input {\n position: relative;\n width: 100%;\n\n --input-notch-height: 1.75em;\n --input-icon-size: 1.2em;\n --input-icon-offset: 0.6em;\n --input-icon-gutter: 2.2em;\n\n /* The visible border lives on `fieldset`/`container` below, not this root — buried inside a\n `<label>`, out of reach of a plain ancestor selector. --group-radius-* are custom properties\n (not defined here) that a wrapping ButtonGroup sets per corner on the position it occupies;\n unlike border-radius itself they inherit through any nesting depth, so they reach these\n descendants and flatten the right corners when this Input sits inside a ButtonGroup. */\n --input-radius: var(--group-radius-tl, var(--default-border-radius))\n var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n var(--group-radius-bl, var(--default-border-radius));\n\n &-placeholder {\n position: absolute;\n top: 50%;\n left: 7px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n user-select: none;\n pointer-events: none;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n inset: 0 0 auto 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n padding: 0;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--input-radius);\n font-size: 0.8em;\n inset: calc(var(--input-notch-height) / -2) 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--input-notch-height);\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--input-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-iconLeft {\n .input-matLabel {\n left: var(--input-icon-gutter);\n\n &-floating {\n left: calc(var(--input-icon-gutter) + 0.5em);\n }\n }\n\n .input-placeholder {\n left: var(--input-icon-gutter);\n }\n\n .input-fieldset {\n padding-left: var(--input-icon-gutter);\n }\n }\n\n &-container {\n display: flex;\n align-items: center;\n border-radius: var(--input-radius);\n background-color: var(--input-background);\n transition: border-color;\n cursor: text;\n height: 100%;\n\n &-multiline {\n overflow: hidden;\n padding-top: 10px;\n align-items: flex-start;\n }\n\n &-border {\n transition: border-color;\n background-color: var(--input-background);\n outline: none;\n\n &-focused {\n outline-color: var(--focus-border-color, var(--primary-color));\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-searchFocused {\n outline-color: var(--search-input-color);\n outline-width: 2px;\n border-color: var(--search-input-color);\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n }\n\n &-label {\n display: flex;\n color: var(--text-color);\n height: 100%;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n margin-right: 5px;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n color: color(var(--border-color) lightness(40%));\n background-color: transparent;\n font-size: 12px;\n position: absolute;\n top: -15px;\n white-space: nowrap;\n }\n }\n\n &-upper-right-label {\n position: absolute;\n transform: translateY(calc(-100% - 1.5px));\n right: 8px;\n min-height: 12px;\n font-size: 12px;\n color: color(var(--border-color) lightness(40%));\n }\n\n &-field {\n appearance: none;\n -webkit-appearance: none;\n font-family: inherit;\n font-size: inherit;\n box-sizing: border-box;\n resize: none;\n width: 100%;\n border: none;\n border-radius: var(--default-border-radius);\n overflow: hidden;\n line-height: 1.25;\n color: var(--text-color);\n background: transparent;\n\n &:focus {\n outline: none;\n }\n\n &::placeholder {\n color: var(--text-light);\n }\n\n /* Chrome overrides `background` and `color` on an autofilled field outright. Clipping the\n background to the glyphs is what removes it: the page cannot win on colour, but it can\n say where a background paints, and -webkit-text-fill-color then beats the text colour.\n The field is transparent either way, so what shows through is whatever is really behind\n it — no surface has to declare itself, which an inset-shadow cover-up would have needed. */\n &:-webkit-autofill,\n &:-webkit-autofill:hover,\n &:-webkit-autofill:focus,\n &:-webkit-autofill:active {\n animation-name: inputAutofillStart;\n animation-duration: 1ms;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: var(--text-color);\n caret-color: var(--text-color);\n }\n\n &:not(:-webkit-autofill) {\n animation-name: inputAutofillEnd;\n animation-duration: 1ms;\n }\n\n /* The browser's own search-cancel decoration doesn't match the theme — Search has its own\n themed clear icon (see isSearchCleared in input.tsx) that replaces it. */\n &::-webkit-search-cancel-button,\n &::-webkit-search-decoration {\n -webkit-appearance: none;\n appearance: none;\n display: none;\n }\n\n &-withIcon {\n padding-right: var(--input-icon-gutter) !important;\n\n &-left {\n padding-right: 0.643em !important;\n padding-left: var(--input-icon-gutter) !important;\n }\n }\n\n &-suffix {\n padding-right: 9px;\n }\n\n &-date {\n padding: 6px 9px;\n color-scheme: dark;\n\n svg {\n color: var(--input-icon-color);\n }\n }\n\n &-default {\n padding: 8px 7.5px 6px;\n }\n\n &[disabled] {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n opacity: 1;\n\n &::placeholder {\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n }\n }\n\n &-readOnly {\n cursor: default;\n color: var(--text-dark);\n -webkit-text-fill-color: var(--text-dark);\n }\n\n &-uppercase {\n text-transform: uppercase;\n }\n\n &-multiline {\n overflow-y: auto;\n resize: vertical;\n padding-top: 4px;\n min-height: 30px;\n\n &-autoResize {\n resize: none;\n overflow: hidden;\n }\n }\n\n &-icon {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: transparent;\n border: none;\n position: absolute;\n outline: none;\n right: var(--input-icon-offset);\n color: var(--input-icon-color);\n width: var(--input-icon-size) !important;\n height: var(--input-icon-size) !important;\n user-select: none;\n top: 50%;\n transform: translateY(-50%);\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n &-left {\n left: var(--input-icon-offset);\n }\n\n &-notClickable {\n pointer-events: none;\n }\n }\n }\n\n &-error {\n height: 1em;\n padding-top: 3px;\n color: var(--error-font-color);\n font-size: 11px;\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 0.5;\n }\n }\n}\n\n/* The label's glide between resting and floating is the motion; it still moves, just instantly. */\n@media (prefers-reduced-motion: reduce) {\n .input-matLabel,\n .input-placeholder {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{PopoverAlign as
|
|
1
|
+
"use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import{PopoverAlign as r,PopoverPlacement as i}from"../popover/popover.types.mjs";import ee from"../popover/popover.mjs";import a from"../input/input.mjs";import{nextIndex as o,scrollOptionIntoView as s}from"../utils/listNavigation.mjs";import c from"./inputDropdown.module.mjs";import{useCallback as l,useEffect as u,useMemo as te,useRef as d,useState as f}from"react";import p from"classnames";import{jsx as m,jsxs as h}from"react/jsx-runtime";function ne(e,t){return t?t.value??``:e??``}function g(e,t){let n=t.find(t=>t.value===e);return n?n.label??n.value:e}var re={wrap:!1},ie={empty:`No matches`};function _(_){let{label:ae=``,value:v=``,formValidator:y=null,error:b,isRequired:x=!1,items:S=[],allowCustomValue:C=!1,maxItems:w=100,className:T,style:E,classNames:D,styles:O,onSelect:k=()=>null,onChange:A,placeholder:oe=``,readOnly:j=!1,labels:M,...se}=e(`InputDropdown`,_),N=t(`InputDropdown`,M,ie),P=ne(v,y),[,F]=f(0),I=d(y);u(()=>{if(I.current=y,!y)return;let e=()=>F(e=>e+1);return y.registerOnUpdateListener(e),()=>{y.removeOnUpdateListener(e)}},[y]);let L=x||!!y?.validators.find(e=>e.validatorId===`required`),[R,z]=f(()=>g(P,S)),[B,V]=f(P),[H,U]=f(!1),[W,G]=f(null),K=d(null),q=d(null),J=d(null);if(P!==B){V(P);let e=S.find(e=>e.value===P);e?z(e.label??e.value):C&&z(P)}if(!H&&!C&&R===P){let e=g(P,S);e!==R&&z(e)}let Y=te(()=>{let e=R.toLowerCase(),t=[];for(let n of S)if((n.label??n.value).toLowerCase().includes(e)&&(t.push(n),w>0&&t.length>=w))break;return t},[S,R,w]),ce=!C&&R!==``&&R!==g(P,S),X=Y.length===0&&ce,Z=H&&!j&&(Y.length>0||X),Q=O?.dropdown?.fontSize,le=l(e=>{let t=q.current;e&&t&&Q==null&&(e.style.fontSize=window.getComputedStyle(t).fontSize)},[Q]),ue=()=>{j||(K.current&&=(clearTimeout(K.current),null),U(!0))},de=()=>{I.current&&(I.current.touched=!0),K.current=setTimeout(()=>{U(!1),G(null),K.current=null,C||z(g(P,S))},100)};u(()=>{Z&&W!==null&&s(J.current,J.current?.children[W])},[Z,W]);let $=l(e=>{z(e.label??e.value),U(!1),G(null),y&&y.set(e.value),A&&A(e.value),k(e.value)},[y,A,k]),fe=l(e=>{if(j)return;let t=e.key===`ArrowUp`||e.key===`ArrowDown`;if(!Z){t&&(e.preventDefault(),U(!0));return}if(t){e.preventDefault();let t=e.key===`ArrowDown`?1:-1;G(e=>e!==null&&e===(t===1?Y.length-1:0)?null:o(e,t,Y.length,re))}else e.key===`Enter`&&(W!==null&&Y[W]?(e.preventDefault(),$(Y[W])):C&&(U(!1),G(null)))},[Y,W,$,C,j,Z]),pe=h(`div`,{ref:le,className:p(c.dropdown,D?.dropdown),style:O?.dropdown,children:[X&&m(`div`,{className:p(c.dropdownEmpty,D?.empty),style:O?.empty,onMouseDown:e=>e.preventDefault(),children:N.empty}),m(`div`,{ref:J,className:c.dropdownScroll,children:Y.map((e,t)=>m(`div`,{className:p(c.dropdownItem,W===t&&c.dropdownItemHighlighted,D?.item),style:O?.item,onMouseDown:e=>e.preventDefault(),onClick:()=>$(e),children:e.label??e.value},`${t}_${e.label}_${e.value}`))})]});return h(`div`,{...se,ref:q,className:p(c.inputDropdown,D?.root,T),style:{...O?.root,...E},onFocus:ue,onBlur:de,onKeyDown:fe,children:[m(ee,{isOpen:Z,onOpenChange:e=>{e||(U(!1),G(null))},presentation:n.Default,placement:i.Bottom,align:r.Start,offset:0,withArrow:!1,constrainWidth:!1,closeOnTriggerClick:!1,focusOnOpen:!1,classNames:{panel:c.inputDropdownPanel},content:pe,children:m(a,{autoComplete:`off`,label:ae,value:R,readOnly:j,formValidator:null,isRequired:L,isValid:y?!y.touched||!y.hasError():!b,errorMessage:y?y.touched?y.getCurrentErrorMessage()??``:``:b??``,onChange:e=>{if(z(e),G(null),U(!0),C)y&&y.set(e),A&&A(e);else if(e===``)y&&y.set(``),A&&A(``),k(``);else{let t=S.find(t=>(t.label??t.value).toLowerCase()===e.toLowerCase());t&&(y&&y.set(t.value),A&&A(t.value),k(t.value))}},placeholder:oe})}),m(`span`,{className:c.inputDropdownStatus,role:`status`,"aria-live":`polite`,children:Z&&X?N.empty:``})]})}export{_ as default};
|
|
2
2
|
//# sourceMappingURL=inputDropdown.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputDropdown.mjs","names":[],"sources":["../../../../package/common/inputDropdown/inputDropdown.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport styles from './inputDropdown.module.pcss';\nimport Input from '../input';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults } from '../configProvider';\nimport type { InputDropdownItem, InputDropdownProps } from './inputDropdown.types';\nimport { nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\n\nfunction getKeyFromProps(value: string | undefined, formValidator: InputDropdownProps['formValidator']): string {\n return formValidator ? ((formValidator.value as string) ?? '') : (value ?? '');\n}\n\nfunction labelForKey(key: string, items: InputDropdownItem[]): string {\n const match = items.find((item) => item.value === key);\n return match ? (match.label ?? match.value) : key;\n}\n\n/**\n * The suggestions do not wrap: the list is filtered by what is being typed, so running off one\n * end and reappearing at the other reads as the list having changed under the caret.\n */\nconst NO_WRAP = { wrap: false };\n\nexport default function InputDropdown(props: InputDropdownProps) {\n const {\n label = '',\n value = '',\n formValidator = null,\n error,\n isRequired = false,\n items = [],\n allowCustomValue = false,\n maxItems = 100,\n className,\n style,\n classNames,\n styles: slotStyles,\n onSelect = () => null,\n onChange,\n placeholder = '',\n readOnly = false,\n ...rest\n } = useComponentDefaults('InputDropdown', props);\n const currentKey = getKeyFromProps(value, formValidator);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n\n const [inputText, setInputText] = useState(() => labelForKey(currentKey, items));\n const [prevKey, setPrevKey] = useState(currentKey);\n const [isMenuShown, setIsMenuShown] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null);\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const triggerRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const menuFontSizeOverride = slotStyles?.dropdown?.fontSize;\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const menu = anchorRef.current;\n if (!isMenuShown || !trigger || !menu || menuFontSizeOverride != null) return;\n menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n }, [isMenuShown, menuFontSizeOverride]);\n\n // Sync display text when the controlled key changes externally (e.g. parent reset).\n if (currentKey !== prevKey) {\n setPrevKey(currentKey);\n const match = items.find((item) => item.value === currentKey);\n if (match) setInputText(match.label ?? match.value);\n else if (allowCustomValue) setInputText(currentKey);\n }\n\n const currItems = useMemo(() => {\n const needle = inputText.toLowerCase();\n const matches: InputDropdownItem[] = [];\n for (const item of items) {\n if (!(item.label ?? item.value).toLowerCase().includes(needle)) continue;\n matches.push(item);\n if (maxItems > 0 && matches.length >= maxItems) break;\n }\n return matches;\n }, [items, inputText, maxItems]);\n\n const handleFocus = () => {\n if (readOnly) return;\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n setIsMenuShown(true);\n };\n\n const handleBlur = () => {\n if (fvRef.current) fvRef.current.touched = true;\n hideTimeoutRef.current = setTimeout(() => {\n setIsMenuShown(false);\n hideTimeoutRef.current = null;\n // Without custom values, uncommitted typed text is invalid — snap the\n // display back to the currently selected item's label.\n if (!allowCustomValue) setInputText(labelForKey(currentKey, items));\n }, 100);\n };\n\n useEffect(() => {\n if (!isMenuShown || highlightedIndex === null) return;\n scrollOptionIntoView(scrollRef.current, scrollRef.current?.children[highlightedIndex]);\n }, [isMenuShown, highlightedIndex]);\n\n const handleSelect = useCallback(\n (item: InputDropdownItem) => {\n setInputText(item.label ?? item.value);\n setIsMenuShown(false);\n setHighlightedIndex(null);\n if (formValidator) formValidator.set(item.value);\n if (onChange) onChange(item.value);\n onSelect(item.value);\n },\n [formValidator, onChange, onSelect],\n );\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (readOnly) return;\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setHighlightedIndex((idx) => {\n // The text being typed is part of the cycle, so both ends lead back to it and on round\n // to the far end: last → text → first, first → text → last. That is how a browser's own\n // address bar behaves, and it is why this list does not wrap straight around the way\n // Dropdown and MultiSelect do — they have no typed text to pass through.\n const atFarEnd = idx !== null && idx === (step === 1 ? currItems.length - 1 : 0);\n return atFarEnd ? null : nextIndex(idx, step, currItems.length, NO_WRAP);\n });\n } else if (event.key === 'Enter') {\n if (highlightedIndex !== null && currItems[highlightedIndex]) {\n // This Enter picked a suggestion, so it must not also reach the form around the field.\n // Without this the browser's implicit submission runs as well, and choosing from the\n // list submitted the form underneath the user. The blank state deliberately does not\n // do this: Enter there means the text as typed, and submitting is a fair reading of it.\n event.preventDefault();\n handleSelect(currItems[highlightedIndex]);\n } else if (allowCustomValue) {\n setIsMenuShown(false);\n setHighlightedIndex(null);\n }\n }\n },\n [currItems, highlightedIndex, handleSelect, allowCustomValue, readOnly],\n );\n\n const menu = (\n <div ref={anchorRef} className={cx(styles.dropdown, classNames?.dropdown)} style={slotStyles?.dropdown}>\n <div ref={scrollRef} className={styles.dropdownScroll}>\n {currItems.map((item, key) => (\n <div\n key={`${key}_${item.label}_${item.value}`}\n className={cx(\n styles.dropdownItem,\n highlightedIndex === key && styles.dropdownItemHighlighted,\n classNames?.item,\n )}\n style={slotStyles?.item}\n // Prevent the input from blurring before the click resolves.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => handleSelect(item)}\n >\n {item.label ?? item.value}\n </div>\n ))}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={triggerRef}\n className={cx(styles.inputDropdown, classNames?.root, className)}\n style={{ ...slotStyles?.root, ...style }}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={onKeyDown}\n >\n <Popover\n // The field owns the open state: it opens on typing and closes on blur, on\n // a pick, or on Escape. Only a close is taken from Popover, which is the\n // outside click its portaled panel would otherwise not see.\n isOpen={isMenuShown && !readOnly}\n onOpenChange={(next) => {\n if (!next) setIsMenuShown(false);\n }}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={0}\n withArrow={false}\n constrainWidth={false}\n closeOnTriggerClick={false}\n focusOnOpen={false}\n classNames={{ panel: styles.inputDropdownPanel }}\n content={menu}\n >\n <Input\n // A combobox renders its own suggestion list; the browser's autofill dropdown\n // opens on top of it, so two lists compete for the same space.\n autoComplete='off'\n label={label}\n value={inputText}\n readOnly={readOnly}\n formValidator={null}\n isRequired={computedIsRequired}\n isValid={formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error}\n errorMessage={\n formValidator\n ? formValidator.touched\n ? (formValidator.getCurrentErrorMessage() ?? '')\n : ''\n : (error ?? '')\n }\n onChange={(text) => {\n setInputText(text);\n setHighlightedIndex(null);\n setIsMenuShown(true);\n if (allowCustomValue) {\n if (formValidator) formValidator.set(text);\n if (onChange) onChange(text);\n } else if (text === '') {\n if (formValidator) formValidator.set('');\n if (onChange) onChange('');\n onSelect('');\n } else {\n // Emit when the typed text exactly matches an item's label/value.\n const match = items.find((item) => (item.label ?? item.value).toLowerCase() === text.toLowerCase());\n if (match) {\n if (formValidator) formValidator.set(match.value);\n if (onChange) onChange(match.value);\n onSelect(match.value);\n }\n }\n }}\n placeholder={placeholder}\n />\n </Popover>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"inputDropdown.mjs","names":[],"sources":["../../../../package/common/inputDropdown/inputDropdown.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport styles from './inputDropdown.module.pcss';\nimport Input from '../input';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { Presentation } from '../configProvider/presentation.types';\nimport type { InputDropdownItem, InputDropdownLabels, InputDropdownProps } from './inputDropdown.types';\nimport { nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\n\nfunction getKeyFromProps(value: string | undefined, formValidator: InputDropdownProps['formValidator']): string {\n return formValidator ? ((formValidator.value as string) ?? '') : (value ?? '');\n}\n\nfunction labelForKey(key: string, items: InputDropdownItem[]): string {\n const match = items.find((item) => item.value === key);\n return match ? (match.label ?? match.value) : key;\n}\n\n/**\n * The suggestions do not wrap: the list is filtered by what is being typed, so running off one\n * end and reappearing at the other reads as the list having changed under the caret.\n */\nconst NO_WRAP = { wrap: false };\n\nconst DEFAULT_LABELS: Required<InputDropdownLabels> = { empty: 'No matches' };\n\nexport default function InputDropdown(props: InputDropdownProps) {\n const {\n label = '',\n value = '',\n formValidator = null,\n error,\n isRequired = false,\n items = [],\n allowCustomValue = false,\n maxItems = 100,\n className,\n style,\n classNames,\n styles: slotStyles,\n onSelect = () => null,\n onChange,\n placeholder = '',\n readOnly = false,\n labels,\n ...rest\n } = useComponentDefaults('InputDropdown', props);\n const text = useLabels('InputDropdown', labels, DEFAULT_LABELS);\n const currentKey = getKeyFromProps(value, formValidator);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n\n const [inputText, setInputText] = useState(() => labelForKey(currentKey, items));\n const [prevKey, setPrevKey] = useState(currentKey);\n const [isMenuShown, setIsMenuShown] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null);\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const triggerRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Sync display text when the controlled key changes externally (e.g. parent reset).\n if (currentKey !== prevKey) {\n setPrevKey(currentKey);\n const match = items.find((item) => item.value === currentKey);\n if (match) setInputText(match.label ?? match.value);\n else if (allowCustomValue) setInputText(currentKey);\n }\n\n // `items` often arrives after the value, leaving the raw key on display. Not while open, or a key\n // being typed would turn into its label under the caret.\n if (!isMenuShown && !allowCustomValue && inputText === currentKey) {\n const selectedLabel = labelForKey(currentKey, items);\n if (selectedLabel !== inputText) setInputText(selectedLabel);\n }\n\n const currItems = useMemo(() => {\n const needle = inputText.toLowerCase();\n const matches: InputDropdownItem[] = [];\n for (const item of items) {\n if (!(item.label ?? item.value).toLowerCase().includes(needle)) continue;\n matches.push(item);\n if (maxItems > 0 && matches.length >= maxItems) break;\n }\n return matches;\n }, [items, inputText, maxItems]);\n\n // Only text that blur swaps for the selection's label needs a warning. Comparing with the label\n // rather than '' keeps a preset value missing from `items` from showing \"No matches\" unprompted.\n const isDiscardedOnBlur = !allowCustomValue && inputText !== '' && inputText !== labelForKey(currentKey, items);\n const showEmpty = currItems.length === 0 && isDiscardedOnBlur;\n // Closed rather than hidden when empty: a mounted panel stays on the overlay stack, so inside a\n // Modal an invisible list would take the Escape meant for the modal.\n const isOpen = isMenuShown && !readOnly && (currItems.length > 0 || showEmpty);\n\n // A ref callback rather than a layout effect: the list is portaled out of the field's font size,\n // and Popover positions it in its own layout effect, which runs before this component's would.\n const menuFontSizeOverride = slotStyles?.dropdown?.fontSize;\n const menuRef = useCallback(\n (menu: HTMLDivElement | null) => {\n const trigger = triggerRef.current;\n if (!menu || !trigger || menuFontSizeOverride != null) return;\n menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n },\n [menuFontSizeOverride],\n );\n\n const handleFocus = () => {\n if (readOnly) return;\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n setIsMenuShown(true);\n };\n\n const handleBlur = () => {\n if (fvRef.current) fvRef.current.touched = true;\n hideTimeoutRef.current = setTimeout(() => {\n setIsMenuShown(false);\n setHighlightedIndex(null);\n hideTimeoutRef.current = null;\n // Without custom values, uncommitted typed text is invalid — snap the\n // display back to the currently selected item's label.\n if (!allowCustomValue) setInputText(labelForKey(currentKey, items));\n }, 100);\n };\n\n useEffect(() => {\n if (!isOpen || highlightedIndex === null) return;\n scrollOptionIntoView(scrollRef.current, scrollRef.current?.children[highlightedIndex]);\n }, [isOpen, highlightedIndex]);\n\n const handleSelect = useCallback(\n (item: InputDropdownItem) => {\n setInputText(item.label ?? item.value);\n setIsMenuShown(false);\n setHighlightedIndex(null);\n if (formValidator) formValidator.set(item.value);\n if (onChange) onChange(item.value);\n onSelect(item.value);\n },\n [formValidator, onChange, onSelect],\n );\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (readOnly) return;\n const isArrow = event.key === 'ArrowUp' || event.key === 'ArrowDown';\n // Enter belongs to the form while closed, or it would pick a suggestion the user never saw.\n if (!isOpen) {\n if (isArrow) {\n event.preventDefault();\n setIsMenuShown(true);\n }\n return;\n }\n if (isArrow) {\n event.preventDefault();\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setHighlightedIndex((idx) => {\n // The text being typed is part of the cycle, so both ends lead back to it and on round\n // to the far end: last → text → first, first → text → last. That is how a browser's own\n // address bar behaves, and it is why this list does not wrap straight around the way\n // Dropdown and MultiSelect do — they have no typed text to pass through.\n const atFarEnd = idx !== null && idx === (step === 1 ? currItems.length - 1 : 0);\n return atFarEnd ? null : nextIndex(idx, step, currItems.length, NO_WRAP);\n });\n } else if (event.key === 'Enter') {\n if (highlightedIndex !== null && currItems[highlightedIndex]) {\n // This Enter picked a suggestion, so it must not also reach the form around the field.\n // Without this the browser's implicit submission runs as well, and choosing from the\n // list submitted the form underneath the user. The blank state deliberately does not\n // do this: Enter there means the text as typed, and submitting is a fair reading of it.\n event.preventDefault();\n handleSelect(currItems[highlightedIndex]);\n } else if (allowCustomValue) {\n setIsMenuShown(false);\n setHighlightedIndex(null);\n }\n }\n },\n [currItems, highlightedIndex, handleSelect, allowCustomValue, readOnly, isOpen],\n );\n\n const menu = (\n <div ref={menuRef} className={cx(styles.dropdown, classNames?.dropdown)} style={slotStyles?.dropdown}>\n {showEmpty && (\n <div\n className={cx(styles.dropdownEmpty, classNames?.empty)}\n style={slotStyles?.empty}\n // A blur would throw the typed text away.\n onMouseDown={(e) => e.preventDefault()}\n >\n {text.empty}\n </div>\n )}\n <div ref={scrollRef} className={styles.dropdownScroll}>\n {currItems.map((item, key) => (\n <div\n key={`${key}_${item.label}_${item.value}`}\n className={cx(\n styles.dropdownItem,\n highlightedIndex === key && styles.dropdownItemHighlighted,\n classNames?.item,\n )}\n style={slotStyles?.item}\n // Prevent the input from blurring before the click resolves.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => handleSelect(item)}\n >\n {item.label ?? item.value}\n </div>\n ))}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={triggerRef}\n className={cx(styles.inputDropdown, classNames?.root, className)}\n style={{ ...slotStyles?.root, ...style }}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={onKeyDown}\n >\n <Popover\n // The field owns the open state: it opens on typing and closes on blur, on\n // a pick, or on Escape. Only a close is taken from Popover, which is the\n // outside click its portaled panel would otherwise not see.\n isOpen={isOpen}\n onOpenChange={(next) => {\n if (next) return;\n setIsMenuShown(false);\n setHighlightedIndex(null);\n }}\n // Never a sheet, whatever the app-wide `presentation`: its backdrop would cover the field\n // being typed into.\n presentation={Presentation.Default}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={0}\n withArrow={false}\n constrainWidth={false}\n closeOnTriggerClick={false}\n focusOnOpen={false}\n classNames={{ panel: styles.inputDropdownPanel }}\n content={menu}\n >\n <Input\n // A combobox renders its own suggestion list; the browser's autofill dropdown\n // opens on top of it, so two lists compete for the same space.\n autoComplete='off'\n label={label}\n value={inputText}\n readOnly={readOnly}\n formValidator={null}\n isRequired={computedIsRequired}\n isValid={formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error}\n errorMessage={\n formValidator\n ? formValidator.touched\n ? (formValidator.getCurrentErrorMessage() ?? '')\n : ''\n : (error ?? '')\n }\n onChange={(typed) => {\n setInputText(typed);\n setHighlightedIndex(null);\n setIsMenuShown(true);\n if (allowCustomValue) {\n if (formValidator) formValidator.set(typed);\n if (onChange) onChange(typed);\n } else if (typed === '') {\n if (formValidator) formValidator.set('');\n if (onChange) onChange('');\n onSelect('');\n } else {\n // Emit when the typed text exactly matches an item's label/value.\n const match = items.find((item) => (item.label ?? item.value).toLowerCase() === typed.toLowerCase());\n if (match) {\n if (formValidator) formValidator.set(match.value);\n if (onChange) onChange(match.value);\n onSelect(match.value);\n }\n }\n }}\n placeholder={placeholder}\n />\n </Popover>\n {/* Outside the portal and always mounted: a live region that mounts already filled is not\n reliably announced. */}\n <span className={styles.inputDropdownStatus} role='status' aria-live='polite'>\n {isOpen && showEmpty ? text.empty : ''}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";var e=`inputDropdown_UdYMa`,t=`
|
|
1
|
+
"use client";var e=`inputDropdown_UdYMa`,t=`inputDropdownStatus_ORHlt`,n=`inputDropdownPanel_TAURL`,r=`dropdown_-M5-x`,i=`dropdown-scroll_XMZs-`,a=`dropdown-empty_Kx---`,o=`dropdown-item_JRgbt`,s=`dropdown-item-highlighted_1ZENP`,c={inputDropdown:e,inputDropdownStatus:t,inputDropdownPanel:n,dropdown:r,"dropdown-scroll":`dropdown-scroll_XMZs-`,dropdownScroll:i,"dropdown-empty":`dropdown-empty_Kx---`,dropdownEmpty:a,"dropdown-item":`dropdown-item_JRgbt`,dropdownItem:o,"dropdown-item-highlighted":`dropdown-item-highlighted_1ZENP`,dropdownItemHighlighted:s};export{c as default,r as dropdown,a as dropdownEmpty,o as dropdownItem,s as dropdownItemHighlighted,i as dropdownScroll,e as inputDropdown,n as inputDropdownPanel,t as inputDropdownStatus};
|
|
2
2
|
//# sourceMappingURL=inputDropdown.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputDropdown.module.mjs","names":[],"sources":["../../../../package/common/inputDropdown/inputDropdown.module.pcss"],"sourcesContent":[".inputDropdown {\n position: relative;\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.inputDropdownPanel {\n padding: 0;\n}\n\n/* Chrome, placement and the portal come from the Popover this renders inside; the\n width is this component's own, since the list sizes to the longest suggestion\n rather than to the field. */\n.dropdown {\n width: fit-content;\n max-width: 15em;\n box-sizing: border-box;\n overflow: hidden;\n border-radius: inherit;\n\n &-scroll {\n overflow: auto;\n max-height: 12.5em;\n }\n\n &-item {\n padding: 0.5em;\n z-index: 0;\n outline: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n\n &-highlighted {\n background: var(--background-accent);\n }\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"inputDropdown.module.mjs","names":[],"sources":["../../../../package/common/inputDropdown/inputDropdown.module.pcss"],"sourcesContent":[".inputDropdown {\n position: relative;\n}\n\n.inputDropdownStatus {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.inputDropdownPanel {\n padding: 0;\n}\n\n/* Chrome, placement and the portal come from the Popover this renders inside; the\n width is this component's own, since the list sizes to the longest suggestion\n rather than to the field. */\n.dropdown {\n width: fit-content;\n max-width: 15em;\n box-sizing: border-box;\n overflow: hidden;\n border-radius: inherit;\n\n &-scroll {\n overflow: auto;\n max-height: 12.5em;\n }\n\n &-empty {\n padding: 0.5em;\n color: var(--text-dark);\n }\n\n &-item {\n padding: 0.5em;\n z-index: 0;\n outline: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n\n &-highlighted {\n background: var(--background-accent);\n }\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{renderIcon as e}from"../utils/renderIcon.mjs";import{PopoverAlign as t,PopoverPlacement as n}from"../popover/popover.types.mjs";import{useFocusBoundary as r}from"../hooks/useFocusBoundary.mjs";import i from"../popover/popover.mjs";import a
|
|
1
|
+
"use client";import{renderIcon as e}from"../utils/renderIcon.mjs";import{PopoverAlign as t,PopoverPlacement as n}from"../popover/popover.types.mjs";import{useFocusBoundary as r}from"../hooks/useFocusBoundary.mjs";import i from"../popover/popover.mjs";import{formatHotkey as a}from"../hooks/useHotkeys.mjs";import{isMenuAction as o,isMenuHeading as s,isMenuSeparator as c}from"./menu.types.mjs";import l from"./menu.module.mjs";import{cloneElement as u,isValidElement as d,useEffect as f,useId as ee,useRef as p,useState as m}from"react";import h from"classnames";import{jsx as g,jsxs as _}from"react/jsx-runtime";var v=600;function y(e,t,n){for(let r=1;r<=e.length;r++){let i=(t+n*r+e.length*r)%e.length,a=e[i];if(o(a)&&!a.disabled)return i}return-1}function b(e){return e.findIndex(e=>o(e)&&!e.disabled)}function x(e){for(let t=e.length-1;t>=0;t--){let n=e[t];if(o(n)&&!n.disabled)return t}return-1}function S({items:S,children:C,onAction:w,isOpen:T,onOpenChange:E,placement:D=n.Bottom,align:O=t.Start,presentation:k,className:A,style:j,classNames:M,styles:N,...P}){let F=T!==void 0,[I,L]=m(!1),R=F?T:I,[z,B]=m(-1),V=p([]),H=p(null),U=p(null),{restoreFocusToTrigger:W,focusAfterTrigger:G}=r({triggerRef:H,contentRef:U}),K=ee(),q=p(`first`),J=p({query:``,timer:void 0}),Y=p(!1);function X(e,t=!1){e&&(Y.current=t),F||L(e),E?.(e)}let Z=p(S);f(()=>{Z.current=S}),f(()=>{if(!R)return;if(!Y.current){B(-1);return}let e=Z.current,t=q.current===`last`?x(e):b(e);B(t);let n=requestAnimationFrame(()=>{V.current[t]?.focus()});return()=>{cancelAnimationFrame(n),q.current=`first`}},[R]),f(()=>()=>clearTimeout(J.current.timer),[]);function Q(e){e<0||(B(e),V.current[e]?.focus())}function $(e){X(!1),e&&W()}function te(e){if(e.length!==1||!e.trim())return!1;clearTimeout(J.current.timer),J.current.query+=e.toLowerCase(),J.current.timer=setTimeout(()=>{J.current.query=``},v);let t=J.current.query,n=t.length>1&&[...t].every(e=>e===t[0]),r=n?t[0]:t,i=n||t.length===1?z:z-1;for(let e=1;e<=S.length;e++){let t=(i+e+S.length)%S.length,n=S[t];if(o(n)&&!n.disabled&&(V.current[t]?.textContent?.trim().toLowerCase()??``).startsWith(r))return Q(t),!0}return!1}function ne(e){if(e.key===`ArrowDown`||e.key===`ArrowUp`){if(e.preventDefault(),e.stopPropagation(),R){Q(e.key===`ArrowUp`?x(S):b(S));return}q.current=e.key===`ArrowUp`?`last`:`first`,X(!0,!0)}}function re(e){switch(e.key){case`ArrowDown`:Q(y(S,z,1));break;case`ArrowUp`:Q(z<0?x(S):y(S,z,-1));break;case`Home`:Q(b(S));break;case`End`:Q(x(S));break;case`Escape`:$(!0);break;case`Tab`:$(!1),e.shiftKey?W():G();break;default:if(!te(e.key))return}e.preventDefault(),e.stopPropagation()}function ie(e){let t=S[e];o(t)&&!t.disabled&&(t.onClick?.(),w?.(t.id),$(!0))}let ae=d(C)?u(C,{"aria-haspopup":C.props[`aria-haspopup`]??`menu`,"aria-expanded":C.props[`aria-expanded`]??R,"aria-controls":R?C.props[`aria-controls`]??K:C.props[`aria-controls`]}):C,oe=g(`div`,{ref:U,id:K,role:`menu`,className:h(l.menu,M?.menu),style:N?.menu,onKeyDown:re,children:S.map((t,n)=>c(t)?g(`div`,{role:`separator`,className:h(l.menuSeparator,M?.separator),style:N?.separator},`separator-${n}`):s(t)?g(`div`,{role:`presentation`,className:h(l.menuHeading,M?.heading),style:N?.heading,children:t.heading},`heading-${n}`):_(`button`,{ref:e=>{V.current[n]=e},type:`button`,role:`menuitem`,disabled:t.disabled,tabIndex:n===z?0:-1,className:h(l.menuItem,t.danger&&l.menuItemDanger,M?.item),style:N?.item,onClick:()=>ie(n),onMouseEnter:()=>B(n),children:[t.icon!=null&&e(t.icon,{className:h(l.menuItemIcon,M?.itemIcon),style:N?.itemIcon}),g(`span`,{className:h(l.menuItemLabel,M?.itemLabel),style:N?.itemLabel,children:t.label}),t.shortcut!=null&&g(`span`,{className:h(l.menuItemShortcut,M?.itemShortcut),style:N?.itemShortcut,children:a(t.shortcut)})]},t.id))});return g(i,{...P,isOpen:R,onOpenChange:(e,{fromKeyboard:t})=>X(e,t),placement:D,align:O,presentation:k,content:oe,focusOnOpen:!1,constrainWidth:!1,classNames:{panel:l.menuPanel},className:h(A,M?.root),style:{...j,...N?.root},children:g(`span`,{ref:H,className:h(l.menuTrigger,M?.trigger),style:N?.trigger,onKeyDown:ne,children:ae})})}export{S as default};
|
|
2
2
|
//# sourceMappingURL=menu.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu.mjs","names":[],"sources":["../../../../package/common/menu/menu.tsx"],"sourcesContent":["import { cloneElement, isValidElement, useEffect, useId, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport styles from './menu.module.pcss';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useFocusBoundary } from '../hooks/useFocusBoundary';\nimport { isMenuAction, isMenuHeading, isMenuSeparator, type MenuEntry, type MenuProps } from './menu.types';\n\n/** How long a typed run stays one search before it starts over. */\nconst TYPEAHEAD_RESET_MS = 600;\n\n/** Index of the next action that can be focused, wrapping, or -1 if there is none. */\nfunction nextEnabled(items: MenuEntry[], from: number, step: 1 | -1): number {\n for (let i = 1; i <= items.length; i++) {\n // The `items.length * i` term only keeps the value positive for a backwards\n // step; JavaScript's `%` would otherwise return a negative index.\n const index = (from + step * i + items.length * i) % items.length;\n const entry = items[index];\n if (isMenuAction(entry) && !entry.disabled) return index;\n }\n return -1;\n}\n\nfunction firstEnabled(items: MenuEntry[]): number {\n return items.findIndex((entry) => isMenuAction(entry) && !entry.disabled);\n}\n\nfunction lastEnabled(items: MenuEntry[]): number {\n for (let i = items.length - 1; i >= 0; i--) {\n const entry = items[i];\n if (isMenuAction(entry) && !entry.disabled) return i;\n }\n return -1;\n}\n\n/**\n * A menu of actions on a trigger of your choosing.\n *\n * `Popover` does the anchoring, portalling and outside-click work but takes\n * arbitrary `content`, which leaves every consumer to write their own `ul`/`li`\n * and, in practice, to leave out the roles and the arrow keys. This is that list,\n * done once: `role=\"menu\"`, a roving focus that skips disabled entries, Escape\n * back to the trigger, and separators and headings that stay out of the keyboard\n * order.\n */\nfunction Menu({\n items,\n children,\n onAction,\n isOpen: controlledOpen,\n onOpenChange,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Start,\n presentation,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: MenuProps): ReactElement {\n const isControlled = controlledOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(false);\n const isOpen = isControlled ? controlledOpen : internalOpen;\n\n const [activeIndex, setActiveIndex] = useState(-1);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const triggerRef = useRef<HTMLSpanElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n // Tab leaves the panel in both directions, so the menu needs the same boundary\n // Popover uses: back to the trigger, or on to whatever follows it.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({ triggerRef, contentRef: menuRef });\n const menuId = useId();\n // Which end to land on when the menu opens: `ArrowUp` on the trigger opens at\n // the bottom, everything else at the top.\n const openAtRef = useRef<'first' | 'last'>('first');\n const typeahead = useRef<{ query: string; timer: ReturnType<typeof setTimeout> | undefined }>({\n query: '',\n timer: undefined,\n });\n\n // Whether a key opened the menu, which decides between landing the focus on an\n // entry and leaving it on the trigger. Set from Popover, which is what sees the\n // trigger's events, or here when an arrow key did the opening.\n const openedByKeyboardRef = useRef(false);\n\n function setOpen(next: boolean, fromKeyboard = false) {\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }\n\n // Focus follows the active entry rather than the menu keeping focus and\n // pointing at it: the items are real buttons, so Enter and Space are theirs.\n // Popover's own `focusOnOpen` would take the first focusable and stop there;\n // the active index has to move with it, so the menu does it itself.\n useEffect(() => {\n if (!isOpen) return;\n // A pointer open leaves the focus where the pointer put it. A highlight on an\n // entry nobody pointed at reads as a selection, and the mouse is about to land\n // somewhere else anyway; the first arrow key moves in, see the trigger's keys.\n if (!openedByKeyboardRef.current) {\n setActiveIndex(-1);\n return;\n }\n const target = openAtRef.current === 'last' ? lastEnabled(items) : firstEnabled(items);\n setActiveIndex(target);\n // The panel is portalled and animated in, so its buttons exist a frame later.\n const frame = requestAnimationFrame(() => {\n itemRefs.current[target]?.focus();\n });\n return () => {\n cancelAnimationFrame(frame);\n openAtRef.current = 'first';\n };\n }, [isOpen, items]);\n\n useEffect(() => () => clearTimeout(typeahead.current.timer), []);\n\n function moveTo(index: number) {\n if (index < 0) return;\n setActiveIndex(index);\n itemRefs.current[index]?.focus();\n }\n\n function close(returnFocus: boolean) {\n setOpen(false);\n if (returnFocus) restoreFocusToTrigger();\n }\n\n /**\n * Jumps to the next entry whose label starts with what has been typed. The\n * text comes off the rendered button rather than the `label` prop, which is a\n * `ReactNode` and may be an icon, a badge or anything else.\n */\n function handleTypeahead(key: string): boolean {\n if (key.length !== 1 || !key.trim()) return false;\n clearTimeout(typeahead.current.timer);\n typeahead.current.query += key.toLowerCase();\n typeahead.current.timer = setTimeout(() => {\n typeahead.current.query = '';\n }, TYPEAHEAD_RESET_MS);\n\n const query = typeahead.current.query;\n // A repeated single character walks through the entries starting with it,\n // rather than sticking to the first one.\n const repeated = query.length > 1 && [...query].every((c) => c === query[0]);\n const needle = repeated ? query[0] : query;\n const from = repeated || query.length === 1 ? activeIndex : activeIndex - 1;\n\n for (let i = 1; i <= items.length; i++) {\n const index = (from + i + items.length) % items.length;\n const entry = items[index];\n if (!isMenuAction(entry) || entry.disabled) continue;\n const text = itemRefs.current[index]?.textContent?.trim().toLowerCase() ?? '';\n if (text.startsWith(needle)) {\n moveTo(index);\n return true;\n }\n }\n return false;\n }\n\n /** The trigger's own keys, which decide where the menu opens. */\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n // Popover opens on these keys too; letting both act would be harmless but\n // this keeps the decision about where to land in one place.\n event.preventDefault();\n event.stopPropagation();\n // Already open means a pointer opened it and the focus never left the trigger,\n // since an entry's own keys never reach here. This is the key that moves in.\n if (isOpen) {\n moveTo(event.key === 'ArrowUp' ? lastEnabled(items) : firstEnabled(items));\n return;\n }\n openAtRef.current = event.key === 'ArrowUp' ? 'last' : 'first';\n setOpen(true, true);\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n switch (event.key) {\n case 'ArrowDown':\n moveTo(nextEnabled(items, activeIndex, 1));\n break;\n case 'ArrowUp':\n moveTo(nextEnabled(items, activeIndex, -1));\n break;\n case 'Home':\n moveTo(firstEnabled(items));\n break;\n case 'End':\n moveTo(lastEnabled(items));\n break;\n // Popover handles both of these too, but only once the focus has actually\n // reached the panel: its Escape restores focus on that condition, and its\n // Tab handling acts at the first and last focusable. A menu focuses its\n // first entry a frame after opening, so a key pressed in between would\n // fall through. Here they are unconditional, which is also what a menu\n // wants from Tab: leave, whatever is focused.\n case 'Escape':\n close(true);\n break;\n case 'Tab':\n // Carry on from the trigger, as if the panel had never been in the tab\n // order. Closing and letting the browser take it from here would move on\n // from an entry that is about to be removed, which lands at the top of\n // the document.\n close(false);\n if (event.shiftKey) restoreFocusToTrigger();\n else focusAfterTrigger();\n break;\n default:\n if (!handleTypeahead(event.key)) return;\n }\n event.preventDefault();\n event.stopPropagation();\n }\n\n function handleAction(index: number) {\n const entry = items[index];\n if (!isMenuAction(entry) || entry.disabled) return;\n entry.onClick?.();\n onAction?.(entry.id);\n close(true);\n }\n\n // A screen reader has no way to tell that this button opens a menu unless the\n // button itself says so. Only filled in where the consumer has not, so a\n // trigger that describes itself keeps doing that.\n const triggerWithMenuSemantics = isValidElement<Record<string, unknown>>(children)\n ? cloneElement(children, {\n 'aria-haspopup': children.props['aria-haspopup'] ?? 'menu',\n 'aria-expanded': children.props['aria-expanded'] ?? isOpen,\n 'aria-controls': isOpen ? (children.props['aria-controls'] ?? menuId) : children.props['aria-controls'],\n })\n : children;\n\n const content = (\n <div\n ref={menuRef}\n id={menuId}\n role='menu'\n className={cx(styles.menu, classNames?.menu)}\n style={slotStyles?.menu}\n onKeyDown={handleKeyDown}\n >\n {items.map((entry, index) => {\n if (isMenuSeparator(entry))\n return (\n <div\n // Separators and headings carry no id of their own, and reordering\n // the list re-renders it wholesale anyway.\n key={`separator-${index}`}\n role='separator'\n className={cx(styles.menuSeparator, classNames?.separator)}\n style={slotStyles?.separator}\n />\n );\n\n if (isMenuHeading(entry))\n return (\n <div\n key={`heading-${index}`}\n role='presentation'\n className={cx(styles.menuHeading, classNames?.heading)}\n style={slotStyles?.heading}\n >\n {entry.heading}\n </div>\n );\n\n return (\n <button\n key={entry.id}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type='button'\n role='menuitem'\n disabled={entry.disabled}\n // One tab stop for the whole menu: the arrow keys move between items.\n tabIndex={index === activeIndex ? 0 : -1}\n className={cx(styles.menuItem, entry.danger && styles.menuItemDanger, classNames?.item)}\n style={slotStyles?.item}\n onClick={() => handleAction(index)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon != null &&\n renderIcon(entry.icon, {\n className: cx(styles.menuItemIcon, classNames?.itemIcon),\n style: slotStyles?.itemIcon,\n })}\n <span className={cx(styles.menuItemLabel, classNames?.itemLabel)} style={slotStyles?.itemLabel}>\n {entry.label}\n </span>\n {entry.shortcut != null && (\n <span className={cx(styles.menuItemShortcut, classNames?.itemShortcut)} style={slotStyles?.itemShortcut}>\n {entry.shortcut}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n\n return (\n <Popover\n {...rest}\n isOpen={isOpen}\n onOpenChange={(next, { fromKeyboard }) => setOpen(next, fromKeyboard)}\n placement={placement}\n align={align}\n presentation={presentation}\n content={content}\n // The list pads its own rows, so a hover highlight reaches the panel edge,\n // and it sizes itself. Focus is the menu's own business, see above.\n focusOnOpen={false}\n constrainWidth={false}\n classNames={{ panel: styles.menuPanel }}\n className={cx(className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <span\n ref={triggerRef}\n className={cx(styles.menuTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n onKeyDown={handleTriggerKeyDown}\n >\n {triggerWithMenuSemantics}\n </span>\n </Popover>\n );\n}\n\nexport default Menu;\n"],"mappings":"6iBAWA,IAAM,EAAqB,IAG3B,SAAS,EAAY,EAAoB,EAAc,EAAsB,CAC3E,IAAK,IAAI,EAAI,EAAG,GAAK,EAAM,OAAQ,IAAK,CAGtC,IAAM,GAAS,EAAO,EAAO,EAAI,EAAM,OAAS,GAAK,EAAM,OACrD,EAAQ,EAAM,GACpB,GAAI,EAAa,CAAK,GAAK,CAAC,EAAM,SAAU,OAAO,CACrD,CACA,MAAO,EACT,CAEA,SAAS,EAAa,EAA4B,CAChD,OAAO,EAAM,UAAW,GAAU,EAAa,CAAK,GAAK,CAAC,EAAM,QAAQ,CAC1E,CAEA,SAAS,EAAY,EAA4B,CAC/C,IAAK,IAAI,EAAI,EAAM,OAAS,EAAG,GAAK,EAAG,IAAK,CAC1C,IAAM,EAAQ,EAAM,GACpB,GAAI,EAAa,CAAK,GAAK,CAAC,EAAM,SAAU,OAAO,CACrD,CACA,MAAO,EACT,CAYA,SAAS,EAAK,CACZ,QACA,WACA,WACA,OAAQ,EACR,gBACA,YAAY,EAAiB,OAC7B,QAAQ,EAAa,MACrB,eACA,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACuB,CAC1B,IAAM,EAAe,IAAmB,IAAA,GAClC,CAAC,EAAc,GAAmB,EAAS,EAAK,EAChD,EAAS,EAAe,EAAiB,EAEzC,CAAC,EAAa,GAAkB,EAAS,EAAE,EAC3C,EAAW,EAAqC,CAAC,CAAC,EAClD,EAAa,EAAwB,IAAI,EACzC,EAAU,EAAuB,IAAI,EAGrC,CAAE,wBAAuB,qBAAsB,EAAiB,CAAE,aAAY,WAAY,CAAQ,CAAC,EACnG,EAAS,EAAM,EAGf,EAAY,EAAyB,OAAO,EAC5C,EAAY,EAA4E,CAC5F,MAAO,GACP,MAAO,IAAA,EACT,CAAC,EAKK,EAAsB,EAAO,EAAK,EAExC,SAAS,EAAQ,EAAe,EAAe,GAAO,CAChD,IAAM,EAAoB,QAAU,GACnC,GAAc,EAAgB,CAAI,EACvC,KAAe,CAAI,CACrB,CAMA,MAAgB,CACd,GAAI,CAAC,EAAQ,OAIb,GAAI,CAAC,EAAoB,QAAS,CAChC,EAAe,EAAE,EACjB,MACF,CACA,IAAM,EAAS,EAAU,UAAY,OAAS,EAAY,CAAK,EAAI,EAAa,CAAK,EACrF,EAAe,CAAM,EAErB,IAAM,EAAQ,0BAA4B,CACxC,EAAS,QAAQ,EAAO,EAAE,MAAM,CAClC,CAAC,EACD,UAAa,CACX,qBAAqB,CAAK,EAC1B,EAAU,QAAU,OACtB,CACF,EAAG,CAAC,EAAQ,CAAK,CAAC,EAElB,UAAsB,aAAa,EAAU,QAAQ,KAAK,EAAG,CAAC,CAAC,EAE/D,SAAS,EAAO,EAAe,CACzB,EAAQ,IACZ,EAAe,CAAK,EACpB,EAAS,QAAQ,EAAM,EAAE,MAAM,EACjC,CAEA,SAAS,EAAM,EAAsB,CACnC,EAAQ,EAAK,EACT,GAAa,EAAsB,CACzC,CAOA,SAAS,EAAgB,EAAsB,CAC7C,GAAI,EAAI,SAAW,GAAK,CAAC,EAAI,KAAK,EAAG,MAAO,GAC5C,aAAa,EAAU,QAAQ,KAAK,EACpC,EAAU,QAAQ,OAAS,EAAI,YAAY,EAC3C,EAAU,QAAQ,MAAQ,eAAiB,CACzC,EAAU,QAAQ,MAAQ,EAC5B,EAAG,CAAkB,EAErB,IAAM,EAAQ,EAAU,QAAQ,MAG1B,EAAW,EAAM,OAAS,GAAK,CAAC,GAAG,CAAK,CAAC,CAAC,MAAO,GAAM,IAAM,EAAM,EAAE,EACrE,EAAS,EAAW,EAAM,GAAK,EAC/B,EAAO,GAAY,EAAM,SAAW,EAAI,EAAc,EAAc,EAE1E,IAAK,IAAI,EAAI,EAAG,GAAK,EAAM,OAAQ,IAAK,CACtC,IAAM,GAAS,EAAO,EAAI,EAAM,QAAU,EAAM,OAC1C,EAAQ,EAAM,GAChB,GAAC,EAAa,CAAK,GAAK,GAAM,WACrB,EAAS,QAAQ,EAAM,EAAE,aAAa,KAAK,CAAC,CAAC,YAAY,GAAK,GAAA,CAClE,WAAW,CAAM,EAExB,OADA,EAAO,CAAK,EACL,EAEX,CACA,MAAO,EACT,CAGA,SAAS,EAAqB,EAA4B,CACpD,KAAM,MAAQ,aAAe,EAAM,MAAQ,UAO/C,IAJA,EAAM,eAAe,EACrB,EAAM,gBAAgB,EAGlB,EAAQ,CACV,EAAO,EAAM,MAAQ,UAAY,EAAY,CAAK,EAAI,EAAa,CAAK,CAAC,EACzE,MACF,CACA,EAAU,QAAU,EAAM,MAAQ,UAAY,OAAS,QACvD,EAAQ,GAAM,EAAI,CAFlB,CAGF,CAEA,SAAS,GAAc,EAA4B,CACjD,OAAQ,EAAM,IAAd,CACE,IAAK,YACH,EAAO,EAAY,EAAO,EAAa,CAAC,CAAC,EACzC,MACF,IAAK,UACH,EAAO,EAAY,EAAO,EAAa,EAAE,CAAC,EAC1C,MACF,IAAK,OACH,EAAO,EAAa,CAAK,CAAC,EAC1B,MACF,IAAK,MACH,EAAO,EAAY,CAAK,CAAC,EACzB,MAOF,IAAK,SACH,EAAM,EAAI,EACV,MACF,IAAK,MAKH,EAAM,EAAK,EACP,EAAM,SAAU,EAAsB,EACrC,EAAkB,EACvB,MACF,QACE,GAAI,CAAC,EAAgB,EAAM,GAAG,EAAG,MACrC,CACA,EAAM,eAAe,EACrB,EAAM,gBAAgB,CACxB,CAEA,SAAS,GAAa,EAAe,CACnC,IAAM,EAAQ,EAAM,GACf,EAAa,CAAK,GAAK,GAAM,WAClC,EAAM,UAAU,EAChB,IAAW,EAAM,EAAE,EACnB,EAAM,EAAI,EACZ,CAKA,IAAM,GAA2B,EAAwC,CAAQ,EAC7E,EAAa,EAAU,CACrB,gBAAiB,EAAS,MAAM,kBAAoB,OACpD,gBAAiB,EAAS,MAAM,kBAAoB,EACpD,gBAAiB,EAAU,EAAS,MAAM,kBAAoB,EAAU,EAAS,MAAM,gBACzF,CAAC,EACD,EAEE,GACJ,EAAC,MAAD,CACE,IAAK,EACL,GAAI,EACJ,KAAK,OACL,UAAW,EAAG,EAAO,KAAM,GAAY,IAAI,EAC3C,MAAO,GAAY,KACnB,UAAW,GAEV,SAAA,EAAM,KAAK,EAAO,IACb,EAAgB,CAAK,EAErB,EAAC,MAAD,CAIE,KAAK,YACL,UAAW,EAAG,EAAO,cAAe,GAAY,SAAS,EACzD,MAAO,GAAY,SACpB,EAJM,aAAa,GAInB,EAGD,EAAc,CAAK,EAEnB,EAAC,MAAD,CAEE,KAAK,eACL,UAAW,EAAG,EAAO,YAAa,GAAY,OAAO,EACrD,MAAO,GAAY,QAElB,SAAA,EAAM,OACJ,EANE,WAAW,GAMb,EAIP,EAAC,SAAD,CAEE,IAAM,GAAO,CACX,EAAS,QAAQ,GAAS,CAC5B,EACA,KAAK,SACL,KAAK,WACL,SAAU,EAAM,SAEhB,SAAU,IAAU,EAAc,EAAI,GACtC,UAAW,EAAG,EAAO,SAAU,EAAM,QAAU,EAAO,eAAgB,GAAY,IAAI,EACtF,MAAO,GAAY,KACnB,YAAe,GAAa,CAAK,EACjC,iBAAoB,EAAe,CAAK,EAb1C,SAAA,CAeG,EAAM,MAAQ,MACb,EAAW,EAAM,KAAM,CACrB,UAAW,EAAG,EAAO,aAAc,GAAY,QAAQ,EACvD,MAAO,GAAY,QACrB,CAAC,EACH,EAAC,OAAD,CAAM,UAAW,EAAG,EAAO,cAAe,GAAY,SAAS,EAAG,MAAO,GAAY,UAClF,SAAA,EAAM,KACH,CAAA,EACL,EAAM,UAAY,MACjB,EAAC,OAAD,CAAM,UAAW,EAAG,EAAO,iBAAkB,GAAY,YAAY,EAAG,MAAO,GAAY,aACxF,SAAA,EAAM,QACH,CAAA,CAEF,CA3BD,EAAA,EAAM,EA2BL,CAEX,CACE,CAAA,EAGP,OACE,EAAC,EAAD,CACE,GAAI,EACI,SACR,cAAe,EAAM,CAAE,kBAAmB,EAAQ,EAAM,CAAY,EACzD,YACJ,QACO,eACL,WAGT,YAAa,GACb,eAAgB,GAChB,WAAY,CAAE,MAAO,EAAO,SAAU,EACtC,UAAW,EAAG,EAAW,GAAY,IAAI,EACzC,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAEvC,SAAA,EAAC,OAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAO,YAAa,GAAY,OAAO,EACrD,MAAO,GAAY,QACnB,UAAW,EAEV,SAAA,EACG,CAAA,CACC,CAAA,CAEb"}
|
|
1
|
+
{"version":3,"file":"menu.mjs","names":[],"sources":["../../../../package/common/menu/menu.tsx"],"sourcesContent":["import { cloneElement, isValidElement, useEffect, useId, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport styles from './menu.module.pcss';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useFocusBoundary } from '../hooks/useFocusBoundary';\nimport { formatHotkey } from '../hooks/useHotkeys';\nimport { isMenuAction, isMenuHeading, isMenuSeparator, type MenuEntry, type MenuProps } from './menu.types';\n\n/** How long a typed run stays one search before it starts over. */\nconst TYPEAHEAD_RESET_MS = 600;\n\n/** Index of the next action that can be focused, wrapping, or -1 if there is none. */\nfunction nextEnabled(items: MenuEntry[], from: number, step: 1 | -1): number {\n for (let i = 1; i <= items.length; i++) {\n // The `items.length * i` term only keeps the value positive for a backwards\n // step; JavaScript's `%` would otherwise return a negative index.\n const index = (from + step * i + items.length * i) % items.length;\n const entry = items[index];\n if (isMenuAction(entry) && !entry.disabled) return index;\n }\n return -1;\n}\n\nfunction firstEnabled(items: MenuEntry[]): number {\n return items.findIndex((entry) => isMenuAction(entry) && !entry.disabled);\n}\n\nfunction lastEnabled(items: MenuEntry[]): number {\n for (let i = items.length - 1; i >= 0; i--) {\n const entry = items[i];\n if (isMenuAction(entry) && !entry.disabled) return i;\n }\n return -1;\n}\n\n/**\n * A menu of actions on a trigger of your choosing.\n *\n * `Popover` does the anchoring, portalling and outside-click work but takes\n * arbitrary `content`, which leaves every consumer to write their own `ul`/`li`\n * and, in practice, to leave out the roles and the arrow keys. This is that list,\n * done once: `role=\"menu\"`, a roving focus that skips disabled entries, Escape\n * back to the trigger, and separators and headings that stay out of the keyboard\n * order.\n */\nfunction Menu({\n items,\n children,\n onAction,\n isOpen: controlledOpen,\n onOpenChange,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Start,\n presentation,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: MenuProps): ReactElement {\n const isControlled = controlledOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(false);\n const isOpen = isControlled ? controlledOpen : internalOpen;\n\n const [activeIndex, setActiveIndex] = useState(-1);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const triggerRef = useRef<HTMLSpanElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n // Tab leaves the panel in both directions, so the menu needs the same boundary\n // Popover uses: back to the trigger, or on to whatever follows it.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({ triggerRef, contentRef: menuRef });\n const menuId = useId();\n // Which end to land on when the menu opens: `ArrowUp` on the trigger opens at\n // the bottom, everything else at the top.\n const openAtRef = useRef<'first' | 'last'>('first');\n const typeahead = useRef<{ query: string; timer: ReturnType<typeof setTimeout> | undefined }>({\n query: '',\n timer: undefined,\n });\n\n // Whether a key opened the menu, which decides between landing the focus on an\n // entry and leaving it on the trigger. Set from Popover, which is what sees the\n // trigger's events, or here when an arrow key did the opening.\n const openedByKeyboardRef = useRef(false);\n\n function setOpen(next: boolean, fromKeyboard = false) {\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n }\n\n // The effect below needs the entries, but must not re-run when the array is rebuilt. A consumer\n // writes `items={[…]}` inline, so it is a new identity on every render of *their* component:\n // keyed off it, any parent re-render while the menu was open threw the focus back to the first\n // entry, mid-navigation. Declared above the effect that reads it, so it has already run.\n const itemsRef = useRef(items);\n useEffect(() => {\n itemsRef.current = items;\n });\n\n // Focus follows the active entry rather than the menu keeping focus and\n // pointing at it: the items are real buttons, so Enter and Space are theirs.\n // Popover's own `focusOnOpen` would take the first focusable and stop there;\n // the active index has to move with it, so the menu does it itself.\n useEffect(() => {\n if (!isOpen) return;\n // A pointer open leaves the focus where the pointer put it. A highlight on an\n // entry nobody pointed at reads as a selection, and the mouse is about to land\n // somewhere else anyway; the first arrow key moves in, see the trigger's keys.\n if (!openedByKeyboardRef.current) {\n setActiveIndex(-1);\n return;\n }\n const entries = itemsRef.current;\n const target = openAtRef.current === 'last' ? lastEnabled(entries) : firstEnabled(entries);\n setActiveIndex(target);\n // The panel is portalled and animated in, so its buttons exist a frame later.\n const frame = requestAnimationFrame(() => {\n itemRefs.current[target]?.focus();\n });\n return () => {\n cancelAnimationFrame(frame);\n openAtRef.current = 'first';\n };\n }, [isOpen]);\n\n useEffect(() => () => clearTimeout(typeahead.current.timer), []);\n\n function moveTo(index: number) {\n if (index < 0) return;\n setActiveIndex(index);\n itemRefs.current[index]?.focus();\n }\n\n function close(returnFocus: boolean) {\n setOpen(false);\n if (returnFocus) restoreFocusToTrigger();\n }\n\n /**\n * Jumps to the next entry whose label starts with what has been typed. The\n * text comes off the rendered button rather than the `label` prop, which is a\n * `ReactNode` and may be an icon, a badge or anything else.\n */\n function handleTypeahead(key: string): boolean {\n if (key.length !== 1 || !key.trim()) return false;\n clearTimeout(typeahead.current.timer);\n typeahead.current.query += key.toLowerCase();\n typeahead.current.timer = setTimeout(() => {\n typeahead.current.query = '';\n }, TYPEAHEAD_RESET_MS);\n\n const query = typeahead.current.query;\n // A repeated single character walks through the entries starting with it,\n // rather than sticking to the first one.\n const repeated = query.length > 1 && [...query].every((c) => c === query[0]);\n const needle = repeated ? query[0] : query;\n const from = repeated || query.length === 1 ? activeIndex : activeIndex - 1;\n\n for (let i = 1; i <= items.length; i++) {\n const index = (from + i + items.length) % items.length;\n const entry = items[index];\n if (!isMenuAction(entry) || entry.disabled) continue;\n const text = itemRefs.current[index]?.textContent?.trim().toLowerCase() ?? '';\n if (text.startsWith(needle)) {\n moveTo(index);\n return true;\n }\n }\n return false;\n }\n\n /** The trigger's own keys, which decide where the menu opens. */\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n // Popover opens on these keys too; letting both act would be harmless but\n // this keeps the decision about where to land in one place.\n event.preventDefault();\n event.stopPropagation();\n // Already open means a pointer opened it and the focus never left the trigger,\n // since an entry's own keys never reach here. This is the key that moves in.\n if (isOpen) {\n moveTo(event.key === 'ArrowUp' ? lastEnabled(items) : firstEnabled(items));\n return;\n }\n openAtRef.current = event.key === 'ArrowUp' ? 'last' : 'first';\n setOpen(true, true);\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n switch (event.key) {\n case 'ArrowDown':\n moveTo(nextEnabled(items, activeIndex, 1));\n break;\n case 'ArrowUp':\n // Stepping back from \"nothing active\" lands two short of the end, so the end is named.\n moveTo(activeIndex < 0 ? lastEnabled(items) : nextEnabled(items, activeIndex, -1));\n break;\n case 'Home':\n moveTo(firstEnabled(items));\n break;\n case 'End':\n moveTo(lastEnabled(items));\n break;\n // Popover handles both of these too, but only once the focus has actually\n // reached the panel: its Escape restores focus on that condition, and its\n // Tab handling acts at the first and last focusable. A menu focuses its\n // first entry a frame after opening, so a key pressed in between would\n // fall through. Here they are unconditional, which is also what a menu\n // wants from Tab: leave, whatever is focused.\n case 'Escape':\n close(true);\n break;\n case 'Tab':\n // Carry on from the trigger, as if the panel had never been in the tab\n // order. Closing and letting the browser take it from here would move on\n // from an entry that is about to be removed, which lands at the top of\n // the document.\n close(false);\n if (event.shiftKey) restoreFocusToTrigger();\n else focusAfterTrigger();\n break;\n default:\n if (!handleTypeahead(event.key)) return;\n }\n event.preventDefault();\n event.stopPropagation();\n }\n\n function handleAction(index: number) {\n const entry = items[index];\n if (!isMenuAction(entry) || entry.disabled) return;\n entry.onClick?.();\n onAction?.(entry.id);\n close(true);\n }\n\n // A screen reader has no way to tell that this button opens a menu unless the\n // button itself says so. Only filled in where the consumer has not, so a\n // trigger that describes itself keeps doing that.\n const triggerWithMenuSemantics = isValidElement<Record<string, unknown>>(children)\n ? cloneElement(children, {\n 'aria-haspopup': children.props['aria-haspopup'] ?? 'menu',\n 'aria-expanded': children.props['aria-expanded'] ?? isOpen,\n 'aria-controls': isOpen ? (children.props['aria-controls'] ?? menuId) : children.props['aria-controls'],\n })\n : children;\n\n const content = (\n <div\n ref={menuRef}\n id={menuId}\n role='menu'\n className={cx(styles.menu, classNames?.menu)}\n style={slotStyles?.menu}\n onKeyDown={handleKeyDown}\n >\n {items.map((entry, index) => {\n if (isMenuSeparator(entry))\n return (\n <div\n // Separators and headings carry no id of their own, and reordering\n // the list re-renders it wholesale anyway.\n key={`separator-${index}`}\n role='separator'\n className={cx(styles.menuSeparator, classNames?.separator)}\n style={slotStyles?.separator}\n />\n );\n\n if (isMenuHeading(entry))\n return (\n <div\n key={`heading-${index}`}\n role='presentation'\n className={cx(styles.menuHeading, classNames?.heading)}\n style={slotStyles?.heading}\n >\n {entry.heading}\n </div>\n );\n\n return (\n <button\n key={entry.id}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type='button'\n role='menuitem'\n disabled={entry.disabled}\n // One tab stop for the whole menu: the arrow keys move between items.\n tabIndex={index === activeIndex ? 0 : -1}\n className={cx(styles.menuItem, entry.danger && styles.menuItemDanger, classNames?.item)}\n style={slotStyles?.item}\n onClick={() => handleAction(index)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon != null &&\n renderIcon(entry.icon, {\n className: cx(styles.menuItemIcon, classNames?.itemIcon),\n style: slotStyles?.itemIcon,\n })}\n <span className={cx(styles.menuItemLabel, classNames?.itemLabel)} style={slotStyles?.itemLabel}>\n {entry.label}\n </span>\n {entry.shortcut != null && (\n <span className={cx(styles.menuItemShortcut, classNames?.itemShortcut)} style={slotStyles?.itemShortcut}>\n {formatHotkey(entry.shortcut)}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n\n return (\n <Popover\n {...rest}\n isOpen={isOpen}\n onOpenChange={(next, { fromKeyboard }) => setOpen(next, fromKeyboard)}\n placement={placement}\n align={align}\n presentation={presentation}\n content={content}\n // The list pads its own rows, so a hover highlight reaches the panel edge,\n // and it sizes itself. Focus is the menu's own business, see above.\n focusOnOpen={false}\n constrainWidth={false}\n classNames={{ panel: styles.menuPanel }}\n className={cx(className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <span\n ref={triggerRef}\n className={cx(styles.menuTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n onKeyDown={handleTriggerKeyDown}\n >\n {triggerWithMenuSemantics}\n </span>\n </Popover>\n );\n}\n\nexport default Menu;\n"],"mappings":"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"}
|