@ahrowe/ui 0.27.0 → 0.28.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/dist/esm/common/accordion/accordion.mjs +1 -1
- package/dist/esm/common/accordion/accordion.mjs.map +1 -1
- package/dist/esm/common/colorPicker/colorPicker.mjs +1 -1
- package/dist/esm/common/colorPicker/colorPicker.mjs.map +1 -1
- package/dist/esm/common/confirmModal/confirmModal.mjs +1 -1
- package/dist/esm/common/confirmModal/confirmModal.mjs.map +1 -1
- package/dist/esm/common/dropZone/dropZone.mjs +1 -1
- package/dist/esm/common/dropZone/dropZone.mjs.map +1 -1
- package/dist/esm/common/floatingMenu/useFloatingPosition.mjs +1 -1
- package/dist/esm/common/floatingMenu/useFloatingPosition.mjs.map +1 -1
- package/dist/esm/common/hooks/useAnchorTracking.mjs +2 -0
- package/dist/esm/common/hooks/useAnchorTracking.mjs.map +1 -0
- package/dist/esm/common/idleManager/idleManager.mjs +1 -1
- package/dist/esm/common/idleManager/idleManager.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/inputDropdown/inputDropdown.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
- package/dist/esm/common/klipyPicker/components/gifView/gifView.mjs +1 -1
- package/dist/esm/common/klipyPicker/components/gifView/gifView.mjs.map +1 -1
- package/dist/esm/common/multiSelect/multiSelect.mjs +2 -0
- package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -0
- package/dist/esm/common/multiSelect/multiSelect.module.mjs +2 -0
- package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -0
- package/dist/esm/common/overscroll/overscroll.mjs +1 -1
- package/dist/esm/common/overscroll/overscroll.mjs.map +1 -1
- package/dist/esm/common/pagination/pagination.mjs +2 -0
- package/dist/esm/common/pagination/pagination.mjs.map +1 -0
- package/dist/esm/common/pagination/pagination.module.mjs +2 -0
- package/dist/esm/common/pagination/pagination.module.mjs.map +1 -0
- package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
- package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/tree/flattenTree.mjs +2 -0
- package/dist/esm/common/tree/flattenTree.mjs.map +1 -0
- package/dist/esm/common/tree/tree.mjs +2 -0
- package/dist/esm/common/tree/tree.mjs.map +1 -0
- package/dist/esm/common/tree/tree.module.mjs +2 -0
- package/dist/esm/common/tree/tree.module.mjs.map +1 -0
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualRow.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/services/formValidation/validatableComponent.mjs.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/package/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/types/package/common/floatingMenu/useFloatingPosition.d.ts +4 -6
- package/dist/types/package/common/hooks/useAnchorTracking.d.ts +40 -0
- package/dist/types/package/common/inputDropdown/inputDropdown.types.d.ts +6 -0
- package/dist/types/package/common/multiSelect/index.d.ts +2 -0
- package/dist/types/package/common/multiSelect/multiSelect.d.ts +4 -0
- package/dist/types/package/common/multiSelect/multiSelect.types.d.ts +62 -0
- package/dist/types/package/common/pagination/index.d.ts +2 -0
- package/dist/types/package/common/pagination/pagination.d.ts +4 -0
- package/dist/types/package/common/pagination/pagination.types.d.ts +30 -0
- package/dist/types/package/common/popover/usePopoverPosition.d.ts +4 -2
- package/dist/types/package/common/tree/flattenTree.d.ts +3 -0
- package/dist/types/package/common/tree/index.d.ts +3 -0
- package/dist/types/package/common/tree/tree.d.ts +4 -0
- package/dist/types/package/common/tree/tree.types.d.ts +62 -0
- package/dist/types/package/common/virtualList/virtualList.types.d.ts +8 -0
- package/dist/types/package/common/virtualList/virtualRow.d.ts +1 -1
- package/dist/types/package/index.d.ts +6 -0
- package/docs/CLAUDE.md +3 -0
- package/docs/InputDropdown.md +3 -0
- package/docs/MultiSelect.md +100 -0
- package/docs/Pagination.md +72 -0
- package/docs/Tree.md +95 -0
- package/docs/VirtualList.md +1 -0
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{AccordionStyleType as e}from"./accordion.types.mjs";import t from"./accordion.module.mjs";import n from"./accordion.primary.module.mjs";import r from"./accordion.plain.module.mjs";import{useComponentDefaults as i}from"../configProvider/useComponentDefaults.mjs";import{useEffect as a,useRef as o,useState as s}from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faChevronDown as u}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";var p={[e.Default]:t,[e.Primary]:n,[e.Plain]:r};function m(t){let{id:n=``,title:r=``,children:m=null,expanded:h=null,onHeaderClicked:g=()=>{},styleType:_=e.Default,startOpen:v=!1,hideHeader:y=!1,className:b,style:x,classNames:S,styles:C,...w}=i(`Accordion`,t),T=p[_],E=o(null),
|
|
1
|
+
import{AccordionStyleType as e}from"./accordion.types.mjs";import t from"./accordion.module.mjs";import n from"./accordion.primary.module.mjs";import r from"./accordion.plain.module.mjs";import{useComponentDefaults as i}from"../configProvider/useComponentDefaults.mjs";import{useEffect as a,useRef as o,useState as s}from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faChevronDown as u}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";var p={[e.Default]:t,[e.Primary]:n,[e.Plain]:r};function m(t){let{id:n=``,title:r=``,children:m=null,expanded:h=null,onHeaderClicked:g=()=>{},styleType:_=e.Default,startOpen:v=!1,hideHeader:y=!1,className:b,style:x,classNames:S,styles:C,...w}=i(`Accordion`,t),T=p[_],E=o(null),D=o(null),[O,k]=s(h??v),[A,j]=s(null);a(()=>{h!==null&&k(h)},[h]),a(()=>{let e=E.current;e&&(e.inert=!O)},[O]),a(()=>{let e=D.current;if(!e)return;let t=new ResizeObserver(()=>j(e.offsetHeight));return t.observe(e),()=>t.unobserve(e)},[]);function M(){let e=!O;g(n,e),h===null&&k(e)}let N={height:`${O?32:24}px`},P={height:O?A??`auto`:`0px`};return f(`div`,{...w,id:n||void 0,className:c(T.accordion,O&&T.accordionActive,b,S?.root),style:{...x,...C?.root},children:[!y&&f(`div`,{className:c(T.accordionHeader,O&&T.accordionHeaderActive,S?.header),style:{...N,...C?.header},onClick:M,role:`button`,tabIndex:0,onKeyDown:({key:e})=>{e===`Enter`&&M()},children:[d(`div`,{className:c(T.accordionTitle,S?.title),style:C?.title,children:r}),d(l,{icon:u,className:c(T.accordionIcon,O&&T.accordionIconActive,S?.icon),style:C?.icon})]}),d(`div`,{ref:E,className:c(T.accordionBody,O&&T.accordionBodyActive,S?.body),style:{...P,...C?.body},children:d(`div`,{className:c(T.accordionBodyInner,S?.bodyInner),style:C?.bodyInner,ref:D,children:m})})]})}export{m as default};
|
|
2
2
|
//# sourceMappingURL=accordion.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accordion.mjs","names":[],"sources":["../../../../package/common/accordion/accordion.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';\n\nimport { AccordionStyleType, type AccordionProps } from './accordion.types';\n\nimport defaultStyles from './accordion.module.pcss';\nimport primaryStyles from './accordion.primary.module.pcss';\nimport plainStyles from './accordion.plain.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\n\nconst STYLES_BY_TYPE: Record<AccordionStyleType, typeof defaultStyles> = {\n [AccordionStyleType.Default]: defaultStyles,\n [AccordionStyleType.Primary]: primaryStyles,\n [AccordionStyleType.Plain]: plainStyles,\n};\n\nfunction Accordion(props: AccordionProps): ReactElement {\n const {\n id = '',\n title = '',\n children = null,\n expanded = null,\n onHeaderClicked = () => {},\n styleType = AccordionStyleType.Default,\n startOpen = false,\n hideHeader = false,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Accordion', props);\n const s = STYLES_BY_TYPE[styleType];\n const bodyRef = useRef<HTMLDivElement>(null);\n\n const [isExpanded, setIsExpanded] = useState(expanded ?? startOpen);\n const [bodyHeight, setBodyHeight] = useState<number | null>(null);\n\n
|
|
1
|
+
{"version":3,"file":"accordion.mjs","names":[],"sources":["../../../../package/common/accordion/accordion.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';\n\nimport { AccordionStyleType, type AccordionProps } from './accordion.types';\n\nimport defaultStyles from './accordion.module.pcss';\nimport primaryStyles from './accordion.primary.module.pcss';\nimport plainStyles from './accordion.plain.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\n\nconst STYLES_BY_TYPE: Record<AccordionStyleType, typeof defaultStyles> = {\n [AccordionStyleType.Default]: defaultStyles,\n [AccordionStyleType.Primary]: primaryStyles,\n [AccordionStyleType.Plain]: plainStyles,\n};\n\nfunction Accordion(props: AccordionProps): ReactElement {\n const {\n id = '',\n title = '',\n children = null,\n expanded = null,\n onHeaderClicked = () => {},\n styleType = AccordionStyleType.Default,\n startOpen = false,\n hideHeader = false,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Accordion', props);\n const s = STYLES_BY_TYPE[styleType];\n const bodyRef = useRef<HTMLDivElement>(null);\n const bodyInnerRef = useRef<HTMLDivElement>(null);\n\n const [isExpanded, setIsExpanded] = useState(expanded ?? startOpen);\n const [bodyHeight, setBodyHeight] = useState<number | null>(null);\n\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n if (expanded !== null) setIsExpanded(expanded);\n }, [expanded]);\n\n useEffect(() => {\n const el = bodyRef.current;\n if (el) el.inert = !isExpanded;\n }, [isExpanded]);\n\n useEffect(() => {\n const el = bodyInnerRef.current;\n if (!el) return;\n const observer = new ResizeObserver(() => setBodyHeight(el.offsetHeight));\n observer.observe(el);\n return () => observer.unobserve(el);\n }, []);\n\n function toggleExpanded() {\n const next = !isExpanded;\n onHeaderClicked(id, next);\n if (expanded === null) setIsExpanded(next);\n }\n\n const headerHeight = 24;\n const headerStyle = { height: `${isExpanded ? headerHeight + headerHeight / 3 : headerHeight}px` };\n const bodyStyle = { height: isExpanded ? (bodyHeight ?? 'auto') : '0px' };\n\n return (\n <div {...rest} id={id || undefined} className={cx(s.accordion, isExpanded && s.accordionActive, className, classNames?.root)} style={{ ...style, ...slotStyles?.root }}>\n {!hideHeader && (\n <div\n className={cx(s.accordionHeader, isExpanded && s.accordionHeaderActive, classNames?.header)}\n style={{ ...headerStyle, ...slotStyles?.header }}\n onClick={toggleExpanded}\n role=\"button\"\n tabIndex={0}\n onKeyDown={({ key }) => { if (key === 'Enter') toggleExpanded(); }}\n >\n <div className={cx(s.accordionTitle, classNames?.title)} style={slotStyles?.title}>{title}</div>\n <FontAwesomeIcon\n icon={arrowIcon}\n className={cx(s.accordionIcon, isExpanded && s.accordionIconActive, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n </div>\n )}\n <div\n ref={bodyRef}\n className={cx(s.accordionBody, isExpanded && s.accordionBodyActive, classNames?.body)}\n style={{ ...bodyStyle, ...slotStyles?.body }}\n >\n <div className={cx(s.accordionBodyInner, classNames?.bodyInner)} style={slotStyles?.bodyInner} ref={bodyInnerRef}>\n {children}\n </div>\n </div>\n </div>\n );\n}\n\nexport default Accordion;\n"],"mappings":"whBAYA,IAAM,EAAmE,EACtE,EAAmB,SAAU,GAC7B,EAAmB,SAAU,GAC7B,EAAmB,OAAQ,CAC9B,EAEA,SAAS,EAAU,EAAqC,CACtD,GAAM,CACN,KAAK,GACL,QAAQ,GACR,WAAW,KACX,WAAW,KACX,sBAAwB,CAAC,EACzB,YAAY,EAAmB,QAC/B,YAAY,GACZ,aAAa,GACb,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACC,EAAqB,YAAa,CAAK,EACrC,EAAI,EAAe,GACnB,EAAU,EAAuB,IAAI,EACrC,EAAe,EAAuB,IAAI,EAE1C,CAAC,EAAY,GAAiB,EAAS,GAAY,CAAS,EAC5D,CAAC,EAAY,GAAiB,EAAwB,IAAI,EAEhE,MAAgB,CAEV,IAAa,MAAM,EAAc,CAAQ,CAC/C,EAAG,CAAC,CAAQ,CAAC,EAEb,MAAgB,CACd,IAAM,EAAK,EAAQ,QACf,IAAI,EAAG,MAAQ,CAAC,EACtB,EAAG,CAAC,CAAU,CAAC,EAEf,MAAgB,CACd,IAAM,EAAK,EAAa,QACxB,GAAI,CAAC,EAAI,OACT,IAAM,EAAW,IAAI,mBAAqB,EAAc,EAAG,YAAY,CAAC,EAExE,OADA,EAAS,QAAQ,CAAE,MACN,EAAS,UAAU,CAAE,CACpC,EAAG,CAAC,CAAC,EAEL,SAAS,GAAiB,CACxB,IAAM,EAAO,CAAC,EACd,EAAgB,EAAI,CAAI,EACpB,IAAa,MAAM,EAAc,CAAI,CAC3C,CAGA,IAAM,EAAc,CAAE,OAAQ,GAAG,EAAa,GAAkC,GAAa,GAAI,EAC3F,EAAY,CAAE,OAAQ,EAAc,GAAc,OAAU,KAAM,EAExE,OACE,EAAC,MAAD,CAAK,GAAI,EAAM,GAAI,GAAM,IAAA,GAAW,UAAW,EAAG,EAAE,UAAW,GAAc,EAAE,gBAAiB,EAAW,GAAY,IAAI,EAAG,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,WAArK,CACG,CAAC,GACA,EAAC,MAAD,CACE,UAAW,EAAG,EAAE,gBAAiB,GAAc,EAAE,sBAAuB,GAAY,MAAM,EAC1F,MAAO,CAAE,GAAG,EAAa,GAAG,GAAY,MAAO,EAC/C,QAAS,EACT,KAAK,SACL,SAAU,EACV,WAAY,CAAE,SAAU,CAAM,IAAQ,SAAS,EAAe,CAAG,WANnE,CAQE,EAAC,MAAD,CAAK,UAAW,EAAG,EAAE,eAAgB,GAAY,KAAK,EAAG,MAAO,GAAY,eAAQ,CAAW,CAAA,EAC/F,EAAC,EAAD,CACE,KAAM,EACN,UAAW,EAAG,EAAE,cAAe,GAAc,EAAE,oBAAqB,GAAY,IAAI,EACpF,MAAO,GAAY,IACpB,CAAA,CACE,IAEP,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAE,cAAe,GAAc,EAAE,oBAAqB,GAAY,IAAI,EACpF,MAAO,CAAE,GAAG,EAAW,GAAG,GAAY,IAAK,WAE3C,EAAC,MAAD,CAAK,UAAW,EAAG,EAAE,mBAAoB,GAAY,SAAS,EAAG,MAAO,GAAY,UAAW,IAAK,EACjG,UACE,CAAA,CACF,CAAA,CACF,GAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"../interactableDiv/index.mjs";import{Align as t}from"../floatingMenu/floatingMenu.types.mjs";import n from"../floatingMenu/floatingMenu.mjs";import r from"../input/input.mjs";import i from"./colorPicker.module.mjs";import{useRef as a,useState as o}from"react";import s from"classnames";import{FontAwesomeIcon as c}from"@fortawesome/react-fontawesome";import{faEyeDropper as l}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var f=typeof window<`u`&&`EyeDropper`in window;async function p(){let e=window.EyeDropper;if(!e)return null;try{return(await new e().open()).sRGBHex}catch{return null}}function m(e){return/^#[0-9a-fA-F]{6}$/.test(e)||/^#[0-9a-fA-F]{8}$/.test(e)}function h(e){return/^#[0-9a-fA-F]{8}$/.test(e)?{hex6:e.slice(0,7),alpha:
|
|
1
|
+
import e from"../interactableDiv/index.mjs";import{Align as t}from"../floatingMenu/floatingMenu.types.mjs";import n from"../floatingMenu/floatingMenu.mjs";import r from"../input/input.mjs";import i from"./colorPicker.module.mjs";import{useRef as a,useState as o}from"react";import s from"classnames";import{FontAwesomeIcon as c}from"@fortawesome/react-fontawesome";import{faEyeDropper as l}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var f=typeof window<`u`&&`EyeDropper`in window;async function p(){let e=window.EyeDropper;if(!e)return null;try{return(await new e().open()).sRGBHex}catch{return null}}function m(e){return/^#[0-9a-fA-F]{6}$/.test(e)||/^#[0-9a-fA-F]{8}$/.test(e)}function h(e){return/^#[0-9a-fA-F]{8}$/.test(e)?{hex6:e.slice(0,7),alpha:parseInt(e.slice(7,9),16)/255*100}:{hex6:e.slice(0,7),alpha:100}}function g(e,t,n){return!n||t>=100?e:`${e}${Math.round(t/100*255).toString(16).padStart(2,`0`)}`}function _(e,t){return`rgba(${parseInt(e.slice(1,3),16)}, ${parseInt(e.slice(3,5),16)}, ${parseInt(e.slice(5,7),16)}, ${t/100})`}function v(e){let t=parseInt(e.slice(1,3),16)/255,n=parseInt(e.slice(3,5),16)/255,r=parseInt(e.slice(5,7),16)/255,i=Math.max(t,n,r),a=i-Math.min(t,n,r),o=0;a!==0&&(o=i===t?(n-r)/a%6:i===n?(r-t)/a+2:(t-n)/a+4,o*=60,o<0&&(o+=360));let s=i===0?0:a/i*100,c=i*100;return{h:o,s,v:c}}function y(e,t,n){let r=t/100,i=n/100,a=i*r,o=a*(1-Math.abs(e/60%2-1)),s=i-a,c=0,l=0,u=0;e<60?(c=a,l=o,u=0):e<120?(c=o,l=a,u=0):e<180?(c=0,l=a,u=o):e<240?(c=0,l=o,u=a):e<300?(c=o,l=0,u=a):(c=a,l=0,u=o);let d=e=>Math.round((e+s)*255).toString(16).padStart(2,`0`);return`#${d(c)}${d(l)}${d(u)}`}function b({hex:e,onChange:t,useInputComponent:n=!1}){let[a,s]=o(e),[c,l]=o(e);e!==c&&(l(e),s(e));function d(e){s(e);let n=e.startsWith(`#`)?e:`#${e}`;m(n)&&t(n)}function f(){m(a.startsWith(`#`)?a:`#${a}`)||s(e)}return n?u(r,{value:a,onChange:e=>d(e),onBlur:f}):u(`input`,{type:`text`,value:a,onChange:e=>d(e.target.value),onBlur:f,className:i.hexInput,spellCheck:!1})}function x({hue:e,sat:t,val:n,alpha:r,onHueChange:o,onSVChange:s,onAlphaChange:m,showInput:_=!0,showAlpha:x=!1}){let S=a(null);function C(e){let t=S.current.getBoundingClientRect(),n=Math.max(0,Math.min(1,(e.clientX-t.left)/t.width)),r=Math.max(0,Math.min(1,(e.clientY-t.top)/t.height));return{s:n*100,v:(1-r)*100}}function w(e){e.currentTarget.setPointerCapture(e.pointerId);let{s:t,v:n}=C(e);s(t,n);let r=e=>{let{s:t,v:n}=C(e);s(t,n)},i=()=>{window.removeEventListener(`pointermove`,r),window.removeEventListener(`pointerup`,i)};window.addEventListener(`pointermove`,r),window.addEventListener(`pointerup`,i)}function T(e){let{hex6:t,alpha:n}=h(e),{h:r,s:i,v:a}=v(t);o(r),s(i,a),x&&m(n)}let E=y(e,t,n),D=x?g(E,r,!0):E;return d(`div`,{className:i.panel,children:[d(`div`,{ref:S,className:i.gradientArea,style:{"--hue":e},onPointerDown:w,children:[u(`div`,{className:i.gradientOverlay}),u(`div`,{className:i.cursor,style:{left:`${t}%`,top:`${100-n}%`}})]}),u(`input`,{type:`range`,min:0,max:359,value:Math.round(e),onChange:e=>o(Number(e.target.value)),className:i.hueSlider}),x&&u(`input`,{type:`range`,min:0,max:100,value:Math.round(r),onChange:e=>m(Number(e.target.value)),className:i.alphaSlider,style:{"--alpha-color":E}}),(_||f)&&d(`div`,{className:i.hexRow,children:[_&&u(b,{hex:D,onChange:T}),f&&u(`button`,{type:`button`,className:i.eyeDropperButton,onClick:async()=>{let e=await p();e&&T(e)},"aria-label":`Pick color from screen`,children:u(c,{icon:l})})]})]})}var S=`#3b82f6`;function C({className:r,value:c=S,onChange:l,showInput:f=!1,showAlpha:p=!1,inline:C=!1,...w}){let[T,E]=o(!1),{hex6:D,alpha:O}=h(m(c)?c:S),k=v(D),[A,j]=o({h:k.h,s:k.s,v:k.v,a:O}),M=a(A),[N,P]=o(c);if(c!==N){P(c);let{hex6:e,alpha:t}=h(m(c)?c:S);if(e!==y(A.h,A.s,A.v)||t!==A.a){let n=v(e);j({h:n.h,s:n.s,v:n.v,a:t})}}let{h:F,s:I,v:L,a:R}=A;function z(e,t,n,r){l?.(g(y(e,t,n),r,p))}function B(e){M.current.h=e,j(t=>({...t,h:e})),z(e,M.current.s,M.current.v,M.current.a)}function V(e,t){M.current.s=e,M.current.v=t,j(n=>({...n,s:e,v:t})),z(M.current.h,e,t,M.current.a)}function H(e){M.current.a=e,j(t=>({...t,a:e})),z(M.current.h,M.current.s,M.current.v,e)}let U=y(F,I,L),W=p?_(U,R):U,G=g(U,R,p),K={hue:F,sat:I,val:L,alpha:R,onHueChange:B,onSVChange:V,onAlphaChange:H,showAlpha:p};return C?u(`div`,{...w,className:s(i.colorPicker,r),children:u(x,{...K,showInput:f})}):d(`div`,{...w,className:s(i.colorPicker,r),children:[u(n,{isOpen:T,onOpenChange:E,dontCloseOnChildClick:!0,align:t.Left,content:u(x,{...K}),children:d(e,{className:i.swatch,children:[u(`div`,{className:i.swatchFill,style:{background:W}}),u(`span`,{className:i.swatchHex,children:G})]})}),f&&u(b,{hex:G,onChange:e=>l?.(e),useInputComponent:!0})]})}export{C as default};
|
|
2
2
|
//# sourceMappingURL=colorPicker.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colorPicker.mjs","names":[],"sources":["../../../../package/common/colorPicker/colorPicker.tsx"],"sourcesContent":["import { useState, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faEyeDropper } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from 'components/common/interactableDiv';\nimport FloatingMenu from 'components/common/floatingMenu';\nimport Input from 'components/common/input';\nimport { Align } from 'components/common/floatingMenu/floatingMenu.types';\nimport type { ColorPickerProps } from './colorPicker.types';\n\nimport styles from './colorPicker.module.pcss';\n\ninterface EyeDropperConstructor {\n new (): { open: (options?: { signal?: AbortSignal }) => Promise<{ sRGBHex: string }> };\n}\n\nconst eyeDropperSupported = typeof window !== 'undefined' && 'EyeDropper' in window;\n\nasync function pickColorFromScreen(): Promise<string | null> {\n const EyeDropperCtor = (window as unknown as { EyeDropper?: EyeDropperConstructor }).EyeDropper;\n if (!EyeDropperCtor) return null;\n try {\n const result = await new EyeDropperCtor().open();\n return result.sRGBHex;\n } catch {\n return null; // user cancelled or picker failed\n }\n}\n\n// ── Color utilities ──────────────────────────────────────────────────────────\n\nfunction isValidHex(s: string): boolean {\n return /^#[0-9a-fA-F]{6}$/.test(s) || /^#[0-9a-fA-F]{8}$/.test(s);\n}\n\n/** Split any valid hex into its 6-digit base and 0-100 alpha. */\nfunction parseHex(hex: string): { hex6: string; alpha: number } {\n if (/^#[0-9a-fA-F]{8}$/.test(hex)) {\n return {\n hex6: hex.slice(0, 7),\n alpha: Math.round((parseInt(hex.slice(7, 9), 16) / 255) * 100),\n };\n }\n return { hex6: hex.slice(0, 7), alpha: 100 };\n}\n\n/** Combine a 6-digit hex + 0-100 alpha into the right output format.\n * Full opacity (alpha === 100) is omitted — 6-digit hex implies opaque. */\nfunction buildValue(hex6: string, alpha: number, showAlpha: boolean): string {\n if (!showAlpha || alpha >= 100) return hex6;\n const a = Math.round((alpha / 100) * 255).toString(16).padStart(2, '0');\n return `${hex6}${a}`;\n}\n\nfunction hexToRgba(hex6: string, alpha: number): string {\n const r = parseInt(hex6.slice(1, 3), 16);\n const g = parseInt(hex6.slice(3, 5), 16);\n const b = parseInt(hex6.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha / 100})`;\n}\n\nfunction hexToHsv(hex6: string): { h: number; s: number; v: number } {\n const r = parseInt(hex6.slice(1, 3), 16) / 255;\n const g = parseInt(hex6.slice(3, 5), 16) / 255;\n const b = parseInt(hex6.slice(5, 7), 16) / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const delta = max - min;\n\n let h = 0;\n if (delta !== 0) {\n if (max === r) h = ((g - b) / delta) % 6;\n else if (max === g) h = (b - r) / delta + 2;\n else h = (r - g) / delta + 4;\n h = Math.round(h * 60);\n if (h < 0) h += 360;\n }\n\n const s = max === 0 ? 0 : Math.round((delta / max) * 100);\n const v = Math.round(max * 100);\n\n return { h, s, v };\n}\n\nfunction hsvToHex(h: number, s: number, v: number): string {\n const sv = s / 100;\n const vv = v / 100;\n const c = vv * sv;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = vv - c;\n\n let r = 0, g = 0, b = 0;\n if (h < 60) { r = c; g = x; b = 0; }\n else if (h < 120) { r = x; g = c; b = 0; }\n else if (h < 180) { r = 0; g = c; b = x; }\n else if (h < 240) { r = 0; g = x; b = c; }\n else if (h < 300) { r = x; g = 0; b = c; }\n else { r = c; g = 0; b = x; }\n\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0');\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\n// ── HexInput ─────────────────────────────────────────────────────────────────\n\ninterface HexInputProps {\n hex: string;\n onChange: (hex: string) => void;\n useInputComponent?: boolean;\n}\n\nfunction HexInput({ hex, onChange, useInputComponent = false }: HexInputProps): ReactElement {\n const [draft, setDraft] = useState(hex);\n\n const [prevHex, setPrevHex] = useState(hex);\n if (hex !== prevHex) {\n setPrevHex(hex);\n setDraft(hex);\n }\n\n function handleChange(raw: string) {\n setDraft(raw);\n const normalized = raw.startsWith('#') ? raw : `#${raw}`;\n if (isValidHex(normalized)) onChange(normalized);\n }\n\n function handleBlur() {\n const normalized = draft.startsWith('#') ? draft : `#${draft}`;\n if (!isValidHex(normalized)) setDraft(hex);\n }\n\n if (useInputComponent) {\n return (\n <Input\n value={draft}\n onChange={(value: string) => handleChange(value)}\n onBlur={handleBlur}\n />\n );\n }\n\n return (\n <input\n type=\"text\"\n value={draft}\n onChange={(e) => handleChange(e.target.value)}\n onBlur={handleBlur}\n className={styles.hexInput}\n spellCheck={false}\n />\n );\n}\n\n// ── Picker panel ─────────────────────────────────────────────────────────────\n\ninterface PickerPanelProps {\n hue: number;\n sat: number;\n val: number;\n alpha: number;\n onHueChange: (h: number) => void;\n onSVChange: (s: number, v: number) => void;\n onAlphaChange: (a: number) => void;\n showInput?: boolean;\n showAlpha?: boolean;\n}\n\nfunction PickerPanel({\n hue, sat, val, alpha,\n onHueChange, onSVChange, onAlphaChange,\n showInput = true, showAlpha = false,\n}: PickerPanelProps): ReactElement {\n const gradientRef = useRef<HTMLDivElement>(null);\n\n function getSVFromEvent(e: { clientX: number; clientY: number }): { s: number; v: number } {\n const rect = gradientRef.current!.getBoundingClientRect();\n const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\n const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));\n return { s: x * 100, v: (1 - y) * 100 };\n }\n\n // Pointer Events (not mouse events) so dragging works for touch/pen too.\n function onGradientPointerDown(e: React.PointerEvent) {\n e.currentTarget.setPointerCapture(e.pointerId);\n const { s, v } = getSVFromEvent(e);\n onSVChange(s, v);\n\n const onMove = (ev: PointerEvent) => {\n const { s, v } = getSVFromEvent(ev);\n onSVChange(s, v);\n };\n const onUp = () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n };\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n }\n\n function onHexChange(normalized: string) {\n const { hex6, alpha: parsedAlpha } = parseHex(normalized);\n const { h, s, v } = hexToHsv(hex6);\n onHueChange(h);\n onSVChange(s, v);\n if (showAlpha) onAlphaChange(parsedAlpha);\n }\n\n const hex6 = hsvToHex(hue, sat, val);\n const hexInputValue = showAlpha ? buildValue(hex6, alpha, true) : hex6;\n\n return (\n <div className={styles.panel}>\n <div\n ref={gradientRef}\n className={styles.gradientArea}\n style={{ '--hue': hue } as React.CSSProperties}\n onPointerDown={onGradientPointerDown}\n >\n <div className={styles.gradientOverlay} />\n <div\n className={styles.cursor}\n style={{ left: `${sat}%`, top: `${100 - val}%` }}\n />\n </div>\n\n <input\n type=\"range\"\n min={0}\n max={359}\n value={hue}\n onChange={(e) => onHueChange(Number(e.target.value))}\n className={styles.hueSlider}\n />\n\n {showAlpha && (\n <input\n type=\"range\"\n min={0}\n max={100}\n value={alpha}\n onChange={(e) => onAlphaChange(Number(e.target.value))}\n className={styles.alphaSlider}\n style={{ '--alpha-color': hex6 } as React.CSSProperties}\n />\n )}\n\n {(showInput || eyeDropperSupported) && (\n <div className={styles.hexRow}>\n {showInput && <HexInput hex={hexInputValue} onChange={onHexChange} />}\n {eyeDropperSupported && (\n <button\n type=\"button\"\n className={styles.eyeDropperButton}\n onClick={async () => {\n const hex = await pickColorFromScreen();\n if (hex) onHexChange(hex);\n }}\n aria-label=\"Pick color from screen\"\n >\n <FontAwesomeIcon icon={faEyeDropper} />\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\n// ── ColorPicker ───────────────────────────────────────────────────────────────\n\nconst DEFAULT_COLOR = '#3b82f6';\n\nfunction ColorPicker({\n className,\n value = DEFAULT_COLOR,\n onChange,\n showInput = false,\n showAlpha = false,\n inline = false,\n ...rest\n}: ColorPickerProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const initSafe = isValidHex(value) ? value : DEFAULT_COLOR;\n const { hex6: initHex6, alpha: initAlpha } = parseHex(initSafe);\n const initHsv = hexToHsv(initHex6);\n\n // Single state object so hue survives when saturation/value hit 0\n // (hexToHsv of any grey returns h=0, which would corrupt hue if derived from props).\n const [hsva, setHsva] = useState({ h: initHsv.h, s: initHsv.s, v: initHsv.v, a: initAlpha });\n\n // Ref mirrors state so drag callbacks never read stale closure values.\n const cur = useRef(hsva);\n\n // Sync from external prop changes: \"derive state during render\" pattern\n // avoids setState-in-effect. We compare the incoming hex against what our\n // current state produces — if they match, the change came from us and we skip.\n const [prevValue, setPrevValue] = useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n const safe = isValidHex(value) ? value : DEFAULT_COLOR;\n const { hex6: incomingHex6, alpha: incomingA } = parseHex(safe);\n const currentHex6 = hsvToHex(hsva.h, hsva.s, hsva.v);\n if (incomingHex6 !== currentHex6 || incomingA !== hsva.a) {\n const hsv = hexToHsv(incomingHex6);\n const next = { h: hsv.h, s: hsv.s, v: hsv.v, a: incomingA };\n setHsva(next);\n }\n }\n\n const { h: hue, s: sat, v: val, a: alpha } = hsva;\n\n function emit(h: number, s: number, v: number, a: number) {\n onChange?.(buildValue(hsvToHex(h, s, v), a, showAlpha));\n }\n\n function handleHueChange(h: number) {\n cur.current.h = h;\n setHsva(prev => ({ ...prev, h }));\n emit(h, cur.current.s, cur.current.v, cur.current.a);\n }\n\n function handleSVChange(s: number, v: number) {\n cur.current.s = s;\n cur.current.v = v;\n setHsva(prev => ({ ...prev, s, v }));\n emit(cur.current.h, s, v, cur.current.a);\n }\n\n function handleAlphaChange(a: number) {\n cur.current.a = a;\n setHsva(prev => ({ ...prev, a }));\n emit(cur.current.h, cur.current.s, cur.current.v, a);\n }\n\n const hex6 = hsvToHex(hue, sat, val);\n const swatchBackground = showAlpha ? hexToRgba(hex6, alpha) : hex6;\n const displayValue = buildValue(hex6, alpha, showAlpha);\n\n const pickerPanelProps: PickerPanelProps = {\n hue, sat, val, alpha,\n onHueChange: handleHueChange,\n onSVChange: handleSVChange,\n onAlphaChange: handleAlphaChange,\n showAlpha,\n };\n\n if (inline) {\n return (\n <div {...rest} className={cx(styles.colorPicker, className)}>\n <PickerPanel {...pickerPanelProps} showInput={showInput} />\n </div>\n );\n }\n\n return (\n <div {...rest} className={cx(styles.colorPicker, className)}>\n <FloatingMenu\n isOpen={isOpen}\n onOpenChange={setIsOpen}\n dontCloseOnChildClick\n align={Align.Left}\n content={<PickerPanel {...pickerPanelProps} />}\n >\n <InteractableDiv className={styles.swatch}>\n <div className={styles.swatchFill} style={{ background: swatchBackground }} />\n <span className={styles.swatchHex}>{displayValue}</span>\n </InteractableDiv>\n </FloatingMenu>\n\n {showInput && (\n <HexInput\n hex={displayValue}\n onChange={(v) => onChange?.(v)}\n useInputComponent\n />\n )}\n </div>\n );\n}\n\nexport default ColorPicker;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"colorPicker.mjs","names":[],"sources":["../../../../package/common/colorPicker/colorPicker.tsx"],"sourcesContent":["import { useState, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faEyeDropper } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from 'components/common/interactableDiv';\nimport FloatingMenu from 'components/common/floatingMenu';\nimport Input from 'components/common/input';\nimport { Align } from 'components/common/floatingMenu/floatingMenu.types';\nimport type { ColorPickerProps } from './colorPicker.types';\n\nimport styles from './colorPicker.module.pcss';\n\ninterface EyeDropperConstructor {\n new (): { open: (options?: { signal?: AbortSignal }) => Promise<{ sRGBHex: string }> };\n}\n\nconst eyeDropperSupported = typeof window !== 'undefined' && 'EyeDropper' in window;\n\nasync function pickColorFromScreen(): Promise<string | null> {\n const EyeDropperCtor = (window as unknown as { EyeDropper?: EyeDropperConstructor }).EyeDropper;\n if (!EyeDropperCtor) return null;\n try {\n const result = await new EyeDropperCtor().open();\n return result.sRGBHex;\n } catch {\n return null; // user cancelled or picker failed\n }\n}\n\n// ── Color utilities ──────────────────────────────────────────────────────────\n\nfunction isValidHex(s: string): boolean {\n return /^#[0-9a-fA-F]{6}$/.test(s) || /^#[0-9a-fA-F]{8}$/.test(s);\n}\n\n/** Split any valid hex into its 6-digit base and 0-100 alpha. */\nfunction parseHex(hex: string): { hex6: string; alpha: number } {\n if (/^#[0-9a-fA-F]{8}$/.test(hex)) {\n return {\n hex6: hex.slice(0, 7),\n alpha: (parseInt(hex.slice(7, 9), 16) / 255) * 100,\n };\n }\n return { hex6: hex.slice(0, 7), alpha: 100 };\n}\n\n/** Combine a 6-digit hex + 0-100 alpha into the right output format.\n * Full opacity (alpha === 100) is omitted — 6-digit hex implies opaque. */\nfunction buildValue(hex6: string, alpha: number, showAlpha: boolean): string {\n if (!showAlpha || alpha >= 100) return hex6;\n const a = Math.round((alpha / 100) * 255).toString(16).padStart(2, '0');\n return `${hex6}${a}`;\n}\n\nfunction hexToRgba(hex6: string, alpha: number): string {\n const r = parseInt(hex6.slice(1, 3), 16);\n const g = parseInt(hex6.slice(3, 5), 16);\n const b = parseInt(hex6.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha / 100})`;\n}\n\nfunction hexToHsv(hex6: string): { h: number; s: number; v: number } {\n const r = parseInt(hex6.slice(1, 3), 16) / 255;\n const g = parseInt(hex6.slice(3, 5), 16) / 255;\n const b = parseInt(hex6.slice(5, 7), 16) / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const delta = max - min;\n\n let h = 0;\n if (delta !== 0) {\n if (max === r) h = ((g - b) / delta) % 6;\n else if (max === g) h = (b - r) / delta + 2;\n else h = (r - g) / delta + 4;\n h *= 60;\n if (h < 0) h += 360;\n }\n\n // Deliberately unrounded: hsvToHex rounds once, at the byte. Rounding here too costs up to a\n // unit per channel, so a value handed in would come back out changed (#3b82f6 -> #3b82f5).\n const s = max === 0 ? 0 : (delta / max) * 100;\n const v = max * 100;\n\n return { h, s, v };\n}\n\nfunction hsvToHex(h: number, s: number, v: number): string {\n const sv = s / 100;\n const vv = v / 100;\n const c = vv * sv;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = vv - c;\n\n let r = 0, g = 0, b = 0;\n if (h < 60) { r = c; g = x; b = 0; }\n else if (h < 120) { r = x; g = c; b = 0; }\n else if (h < 180) { r = 0; g = c; b = x; }\n else if (h < 240) { r = 0; g = x; b = c; }\n else if (h < 300) { r = x; g = 0; b = c; }\n else { r = c; g = 0; b = x; }\n\n const toHex = (n: number) => Math.round((n + m) * 255).toString(16).padStart(2, '0');\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\n// ── HexInput ─────────────────────────────────────────────────────────────────\n\ninterface HexInputProps {\n hex: string;\n onChange: (hex: string) => void;\n useInputComponent?: boolean;\n}\n\nfunction HexInput({ hex, onChange, useInputComponent = false }: HexInputProps): ReactElement {\n const [draft, setDraft] = useState(hex);\n\n const [prevHex, setPrevHex] = useState(hex);\n if (hex !== prevHex) {\n setPrevHex(hex);\n setDraft(hex);\n }\n\n function handleChange(raw: string) {\n setDraft(raw);\n const normalized = raw.startsWith('#') ? raw : `#${raw}`;\n if (isValidHex(normalized)) onChange(normalized);\n }\n\n function handleBlur() {\n const normalized = draft.startsWith('#') ? draft : `#${draft}`;\n if (!isValidHex(normalized)) setDraft(hex);\n }\n\n if (useInputComponent) {\n return (\n <Input\n value={draft}\n onChange={(value: string) => handleChange(value)}\n onBlur={handleBlur}\n />\n );\n }\n\n return (\n <input\n type=\"text\"\n value={draft}\n onChange={(e) => handleChange(e.target.value)}\n onBlur={handleBlur}\n className={styles.hexInput}\n spellCheck={false}\n />\n );\n}\n\n// ── Picker panel ─────────────────────────────────────────────────────────────\n\ninterface PickerPanelProps {\n hue: number;\n sat: number;\n val: number;\n alpha: number;\n onHueChange: (h: number) => void;\n onSVChange: (s: number, v: number) => void;\n onAlphaChange: (a: number) => void;\n showInput?: boolean;\n showAlpha?: boolean;\n}\n\nfunction PickerPanel({\n hue, sat, val, alpha,\n onHueChange, onSVChange, onAlphaChange,\n showInput = true, showAlpha = false,\n}: PickerPanelProps): ReactElement {\n const gradientRef = useRef<HTMLDivElement>(null);\n\n function getSVFromEvent(e: { clientX: number; clientY: number }): { s: number; v: number } {\n const rect = gradientRef.current!.getBoundingClientRect();\n const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\n const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));\n return { s: x * 100, v: (1 - y) * 100 };\n }\n\n // Pointer Events (not mouse events) so dragging works for touch/pen too.\n function onGradientPointerDown(e: React.PointerEvent) {\n e.currentTarget.setPointerCapture(e.pointerId);\n const { s, v } = getSVFromEvent(e);\n onSVChange(s, v);\n\n const onMove = (ev: PointerEvent) => {\n const { s, v } = getSVFromEvent(ev);\n onSVChange(s, v);\n };\n const onUp = () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n };\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n }\n\n function onHexChange(normalized: string) {\n const { hex6, alpha: parsedAlpha } = parseHex(normalized);\n const { h, s, v } = hexToHsv(hex6);\n onHueChange(h);\n onSVChange(s, v);\n if (showAlpha) onAlphaChange(parsedAlpha);\n }\n\n const hex6 = hsvToHex(hue, sat, val);\n const hexInputValue = showAlpha ? buildValue(hex6, alpha, true) : hex6;\n\n return (\n <div className={styles.panel}>\n <div\n ref={gradientRef}\n className={styles.gradientArea}\n style={{ '--hue': hue } as React.CSSProperties}\n onPointerDown={onGradientPointerDown}\n >\n <div className={styles.gradientOverlay} />\n <div\n className={styles.cursor}\n style={{ left: `${sat}%`, top: `${100 - val}%` }}\n />\n </div>\n\n <input\n type=\"range\"\n min={0}\n max={359}\n value={Math.round(hue)}\n onChange={(e) => onHueChange(Number(e.target.value))}\n className={styles.hueSlider}\n />\n\n {showAlpha && (\n <input\n type=\"range\"\n min={0}\n max={100}\n value={Math.round(alpha)}\n onChange={(e) => onAlphaChange(Number(e.target.value))}\n className={styles.alphaSlider}\n style={{ '--alpha-color': hex6 } as React.CSSProperties}\n />\n )}\n\n {(showInput || eyeDropperSupported) && (\n <div className={styles.hexRow}>\n {showInput && <HexInput hex={hexInputValue} onChange={onHexChange} />}\n {eyeDropperSupported && (\n <button\n type=\"button\"\n className={styles.eyeDropperButton}\n onClick={async () => {\n const hex = await pickColorFromScreen();\n if (hex) onHexChange(hex);\n }}\n aria-label=\"Pick color from screen\"\n >\n <FontAwesomeIcon icon={faEyeDropper} />\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\n// ── ColorPicker ───────────────────────────────────────────────────────────────\n\nconst DEFAULT_COLOR = '#3b82f6';\n\nfunction ColorPicker({\n className,\n value = DEFAULT_COLOR,\n onChange,\n showInput = false,\n showAlpha = false,\n inline = false,\n ...rest\n}: ColorPickerProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const initSafe = isValidHex(value) ? value : DEFAULT_COLOR;\n const { hex6: initHex6, alpha: initAlpha } = parseHex(initSafe);\n const initHsv = hexToHsv(initHex6);\n\n // Single state object so hue survives when saturation/value hit 0\n // (hexToHsv of any grey returns h=0, which would corrupt hue if derived from props).\n const [hsva, setHsva] = useState({ h: initHsv.h, s: initHsv.s, v: initHsv.v, a: initAlpha });\n\n // Ref mirrors state so drag callbacks never read stale closure values.\n const cur = useRef(hsva);\n\n // Sync from external prop changes: \"derive state during render\" pattern\n // avoids setState-in-effect. We compare the incoming hex against what our\n // current state produces — if they match, the change came from us and we skip.\n const [prevValue, setPrevValue] = useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n const safe = isValidHex(value) ? value : DEFAULT_COLOR;\n const { hex6: incomingHex6, alpha: incomingA } = parseHex(safe);\n const currentHex6 = hsvToHex(hsva.h, hsva.s, hsva.v);\n if (incomingHex6 !== currentHex6 || incomingA !== hsva.a) {\n const hsv = hexToHsv(incomingHex6);\n const next = { h: hsv.h, s: hsv.s, v: hsv.v, a: incomingA };\n setHsva(next);\n }\n }\n\n const { h: hue, s: sat, v: val, a: alpha } = hsva;\n\n function emit(h: number, s: number, v: number, a: number) {\n onChange?.(buildValue(hsvToHex(h, s, v), a, showAlpha));\n }\n\n function handleHueChange(h: number) {\n cur.current.h = h;\n setHsva(prev => ({ ...prev, h }));\n emit(h, cur.current.s, cur.current.v, cur.current.a);\n }\n\n function handleSVChange(s: number, v: number) {\n cur.current.s = s;\n cur.current.v = v;\n setHsva(prev => ({ ...prev, s, v }));\n emit(cur.current.h, s, v, cur.current.a);\n }\n\n function handleAlphaChange(a: number) {\n cur.current.a = a;\n setHsva(prev => ({ ...prev, a }));\n emit(cur.current.h, cur.current.s, cur.current.v, a);\n }\n\n const hex6 = hsvToHex(hue, sat, val);\n const swatchBackground = showAlpha ? hexToRgba(hex6, alpha) : hex6;\n const displayValue = buildValue(hex6, alpha, showAlpha);\n\n const pickerPanelProps: PickerPanelProps = {\n hue, sat, val, alpha,\n onHueChange: handleHueChange,\n onSVChange: handleSVChange,\n onAlphaChange: handleAlphaChange,\n showAlpha,\n };\n\n if (inline) {\n return (\n <div {...rest} className={cx(styles.colorPicker, className)}>\n <PickerPanel {...pickerPanelProps} showInput={showInput} />\n </div>\n );\n }\n\n return (\n <div {...rest} className={cx(styles.colorPicker, className)}>\n <FloatingMenu\n isOpen={isOpen}\n onOpenChange={setIsOpen}\n dontCloseOnChildClick\n align={Align.Left}\n content={<PickerPanel {...pickerPanelProps} />}\n >\n <InteractableDiv className={styles.swatch}>\n <div className={styles.swatchFill} style={{ background: swatchBackground }} />\n <span className={styles.swatchHex}>{displayValue}</span>\n </InteractableDiv>\n </FloatingMenu>\n\n {showInput && (\n <HexInput\n hex={displayValue}\n onChange={(v) => onChange?.(v)}\n useInputComponent\n />\n )}\n </div>\n );\n}\n\nexport default ColorPicker;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{ButtonStyleType as t}from"../button/button.types.mjs";import n from"../actionButtons/actionButtons.mjs";import r from"../modal/modal.mjs";import{useState as i}from"react";import{jsx as a,jsxs as o}from"react/jsx-runtime";function s(s){let{children:c=null,className:l=``,onClose:u=()=>{},onConfirm:d=()=>{},title:f=``,content:p=null,isOpen:m=void 0,submitLabel:h=`Confirm`,cancelLabel:g=`Cancel`,isDelete:_=!1,actionButtonsProps:v={}}=e(`ConfirmModal`,s),[y,b]=i(!1),[x,S]=i(!1);function C(e=!1){b(!1),u(e)}async function w(){S(!0);try{await d(),C(!0)}finally{S(!1)}}let T=o(r,{title:f,isOpen:m===void 0?y:m,onClose:()=>C(),className:c?void 0:l,children:[p,a(n,{onClose:()=>C(),onSubmit:w,submitLabel:h,cancelLabel:g,submitButtonStyle:_?t.Delete:void 0,isSubmitButtonLoading:x,...v})]});return c?o(`div`,{className:l,children:[a(`span`,{onClick:()=>b(!0),children:c}),T]}):T}export{s as default};
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{ButtonStyleType as t}from"../button/button.types.mjs";import n from"../actionButtons/actionButtons.mjs";import r from"../modal/modal.mjs";import{useState as i}from"react";import{jsx as a,jsxs as o}from"react/jsx-runtime";function s(s){let{children:c=null,className:l=``,onClose:u=()=>{},onConfirm:d=()=>{},title:f=``,content:p=null,isOpen:m=void 0,submitLabel:h=`Confirm`,cancelLabel:g=`Cancel`,isDelete:_=!1,actionButtonsProps:v={}}=e(`ConfirmModal`,s),[y,b]=i(!1),[x,S]=i(!1);function C(e=!1){b(!1),u(e)}async function w(){S(!0);try{await d(),C(!0)}catch{}finally{S(!1)}}let T=o(r,{title:f,isOpen:m===void 0?y:m,onClose:()=>C(),className:c?void 0:l,children:[p,a(n,{onClose:()=>C(),onSubmit:w,submitLabel:h,cancelLabel:g,submitButtonStyle:_?t.Delete:void 0,isSubmitButtonLoading:x,...v})]});return c?o(`div`,{className:l,children:[a(`span`,{onClick:()=>b(!0),children:c}),T]}):T}export{s as default};
|
|
2
2
|
//# sourceMappingURL=confirmModal.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirmModal.mjs","names":[],"sources":["../../../../package/common/confirmModal/confirmModal.tsx"],"sourcesContent":["import { useState, type ReactElement } from 'react';\n\nimport Modal from '../modal';\nimport { ActionButtons } from '../actionButtons';\nimport { ButtonStyleType } from '../button';\nimport { useComponentDefaults } from '../configProvider';\nimport type { ConfirmModalProps } from './confirmModal.types';\n\nfunction ConfirmModal(props: ConfirmModalProps): ReactElement {\n const {\n children = null,\n className = '',\n onClose = () => {},\n onConfirm = () => {},\n title = '',\n content = null,\n isOpen: propIsOpen = undefined,\n submitLabel = 'Confirm',\n cancelLabel = 'Cancel',\n isDelete = false,\n actionButtonsProps = {},\n } = useComponentDefaults('ConfirmModal', props);\n const [internalOpen, setInternalOpen] = useState(false);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n function handleClose(didSubmit = false) {\n setInternalOpen(false);\n onClose(didSubmit);\n }\n\n async function handleConfirm() {\n setIsSubmitting(true);\n try {\n await onConfirm();\n handleClose(true);\n } finally {\n setIsSubmitting(false);\n }\n }\n\n const isOpen = propIsOpen === undefined ? internalOpen : propIsOpen;\n\n const modal = (\n <Modal title={title} isOpen={isOpen} onClose={() => handleClose()} className={children ? undefined : className}>\n {content}\n <ActionButtons\n onClose={() => handleClose()}\n onSubmit={handleConfirm}\n submitLabel={submitLabel}\n cancelLabel={cancelLabel}\n submitButtonStyle={isDelete ? ButtonStyleType.Delete : undefined}\n isSubmitButtonLoading={isSubmitting}\n {...actionButtonsProps}\n />\n </Modal>\n );\n\n if (!children) return modal;\n\n return (\n <div className={className}>\n <span onClick={() => setInternalOpen(true)}>{children}</span>\n {modal}\n </div>\n );\n}\n\nexport default ConfirmModal;\n"],"mappings":"sTAQA,SAAS,EAAa,EAAwC,CAC5D,GAAM,CACN,WAAW,KACX,YAAY,GACZ,cAAgB,CAAC,EACjB,gBAAkB,CAAC,EACnB,QAAQ,GACR,UAAU,KACV,OAAQ,EAAa,IAAA,GACrB,cAAc,UACd,cAAc,SACd,WAAW,GACX,qBAAqB,CAAC,GAClB,EAAqB,eAAgB,CAAK,EACxC,CAAC,EAAc,GAAmB,EAAS,EAAK,EAChD,CAAC,EAAc,GAAmB,EAAS,EAAK,EAEtD,SAAS,EAAY,EAAY,GAAO,CACtC,EAAgB,EAAK,EACrB,EAAQ,CAAS,CACnB,CAEA,eAAe,GAAgB,CAC7B,EAAgB,EAAI,EACpB,GAAI,CACF,MAAM,EAAU,EAChB,EAAY,EAAI,CAClB,QAAU,CACR,EAAgB,EAAK,CACvB,CACF,CAIA,IAAM,EACJ,EAAC,EAAD,CAAc,QAAe,OAHhB,IAAe,IAAA,GAAY,EAAe,EAGlB,YAAe,EAAY,EAAG,UAAW,EAAW,IAAA,GAAY,WAArG,CACG,EACD,EAAC,EAAD,CACE,YAAe,EAAY,EAC3B,SAAU,EACG,cACA,cACb,kBAAmB,EAAW,EAAgB,OAAS,IAAA,GACvD,sBAAuB,EACvB,GAAI,CACL,CAAA,CACI,IAKT,OAFK,EAGH,EAAC,MAAD,CAAgB,qBAAhB,CACE,EAAC,OAAD,CAAM,YAAe,EAAgB,EAAI,EAAI,UAAe,CAAA,EAC3D,CACE,IANe,CAQxB"}
|
|
1
|
+
{"version":3,"file":"confirmModal.mjs","names":[],"sources":["../../../../package/common/confirmModal/confirmModal.tsx"],"sourcesContent":["import { useState, type ReactElement } from 'react';\n\nimport Modal from '../modal';\nimport { ActionButtons } from '../actionButtons';\nimport { ButtonStyleType } from '../button';\nimport { useComponentDefaults } from '../configProvider';\nimport type { ConfirmModalProps } from './confirmModal.types';\n\nfunction ConfirmModal(props: ConfirmModalProps): ReactElement {\n const {\n children = null,\n className = '',\n onClose = () => {},\n onConfirm = () => {},\n title = '',\n content = null,\n isOpen: propIsOpen = undefined,\n submitLabel = 'Confirm',\n cancelLabel = 'Cancel',\n isDelete = false,\n actionButtonsProps = {},\n } = useComponentDefaults('ConfirmModal', props);\n const [internalOpen, setInternalOpen] = useState(false);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n function handleClose(didSubmit = false) {\n setInternalOpen(false);\n onClose(didSubmit);\n }\n\n async function handleConfirm() {\n setIsSubmitting(true);\n try {\n await onConfirm();\n handleClose(true);\n } catch {\n // Nothing is awaiting this handler, so a rejection would surface as an unhandled one.\n // The dialog staying open is the signal that the action did not go through; reporting\n // the failure itself belongs to whoever wrote `onConfirm`.\n } finally {\n setIsSubmitting(false);\n }\n }\n\n const isOpen = propIsOpen === undefined ? internalOpen : propIsOpen;\n\n const modal = (\n <Modal title={title} isOpen={isOpen} onClose={() => handleClose()} className={children ? undefined : className}>\n {content}\n <ActionButtons\n onClose={() => handleClose()}\n onSubmit={handleConfirm}\n submitLabel={submitLabel}\n cancelLabel={cancelLabel}\n submitButtonStyle={isDelete ? ButtonStyleType.Delete : undefined}\n isSubmitButtonLoading={isSubmitting}\n {...actionButtonsProps}\n />\n </Modal>\n );\n\n if (!children) return modal;\n\n return (\n <div className={className}>\n <span onClick={() => setInternalOpen(true)}>{children}</span>\n {modal}\n </div>\n );\n}\n\nexport default ConfirmModal;\n"],"mappings":"sTAQA,SAAS,EAAa,EAAwC,CAC5D,GAAM,CACN,WAAW,KACX,YAAY,GACZ,cAAgB,CAAC,EACjB,gBAAkB,CAAC,EACnB,QAAQ,GACR,UAAU,KACV,OAAQ,EAAa,IAAA,GACrB,cAAc,UACd,cAAc,SACd,WAAW,GACX,qBAAqB,CAAC,GAClB,EAAqB,eAAgB,CAAK,EACxC,CAAC,EAAc,GAAmB,EAAS,EAAK,EAChD,CAAC,EAAc,GAAmB,EAAS,EAAK,EAEtD,SAAS,EAAY,EAAY,GAAO,CACtC,EAAgB,EAAK,EACrB,EAAQ,CAAS,CACnB,CAEA,eAAe,GAAgB,CAC7B,EAAgB,EAAI,EACpB,GAAI,CACF,MAAM,EAAU,EAChB,EAAY,EAAI,CAClB,MAAQ,CAIR,QAAU,CACR,EAAgB,EAAK,CACvB,CACF,CAIA,IAAM,EACJ,EAAC,EAAD,CAAc,QAAe,OAHhB,IAAe,IAAA,GAAY,EAAe,EAGlB,YAAe,EAAY,EAAG,UAAW,EAAW,IAAA,GAAY,WAArG,CACG,EACD,EAAC,EAAD,CACE,YAAe,EAAY,EAC3B,SAAU,EACG,cACA,cACb,kBAAmB,EAAW,EAAgB,OAAS,IAAA,GACvD,sBAAuB,EACvB,GAAI,CACL,CAAA,CACI,IAKT,OAFK,EAGH,EAAC,MAAD,CAAgB,qBAAhB,CACE,EAAC,OAAD,CAAM,YAAe,EAAgB,EAAI,EAAI,UAAe,CAAA,EAC3D,CACE,IANe,CAQxB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DropZoneReturnType as e,DropZoneStatus as t}from"./dropZone.types.mjs";import n from"./dropZone.module.mjs";import{useCallback as r,useEffect as i,useId as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d(e,t){if(!t.length)return!0;let n=e.name.toLowerCase(),r=e.type.toLowerCase();return t.some(e=>{let t=e.trim().toLowerCase();return t?t.startsWith(`.`)?n.endsWith(t):t.endsWith(`/*`)?r.startsWith(t.slice(0,-1)):r===t:!1})}function f(t,n){return n===e.File?Promise.resolve({ok:!0,value:t}):new Promise(r=>{let i=new FileReader;i.onload=e=>{let n=e.target?.result;if(typeof n!=`string`){r({ok:!1});return}r({ok:!0,value:{name:t.name,size:t.size,type:t.type,lastModified:t.lastModified,data:n}})},i.onerror=()=>r({ok:!1}),n===e.Base64?i.readAsDataURL(t):i.readAsText(t)})}function p(e){return new Promise(t=>{if(e.isFile)e.file(e=>t([e]),()=>t([]));else if(e.isDirectory){let n=e.createReader(),r=[],i=()=>{n.readEntries(e=>{e.length?(r.push(...e),i()):Promise.all(r.map(p)).then(e=>t(e.flat()))},()=>t([]))};i()}else t([])})}function m(e,t){let n=e.items;if(n&&n.length){if(t&&typeof n[0].webkitGetAsEntry==`function`){let e=[];for(let t=0;t<n.length;t+=1)if(n[t].kind===`file`){let r=n[t].webkitGetAsEntry();r&&e.push(r)}if(e.length)return Promise.all(e.map(p)).then(e=>e.flat())}let e=[];for(let t=0;t<n.length;t+=1)if(n[t].kind===`file`){let r=n[t].getAsFile();r&&e.push(r)}return Promise.resolve(e)}return Promise.resolve(Array.from(e.files||[]))}function h({children:p,acceptedTypes:h=[],returnType:g=e.File,openPickerOnClick:_=!1,multiple:v=!0,maxFiles:y,maxSize:b,enablePaste:x=!1,allowDirectories:S=!0,capture:C,disabled:w=!1,unstyled:T=!1,showFileNames:E=!1,files:D,onFilesChanged:O,onReject:k,onStatusChanged:A,className:j,style:M,classNames:N,styles:P,...F}){let[I,L]=s(t.Idle),R=o(null),z=a(),B=o(I),V=o(0),H=o(0),U=o({acceptedTypes:h,returnType:g,multiple:v,maxFiles:y,maxSize:b,allowDirectories:S,disabled:w,onFilesChanged:O,onReject:k,onStatusChanged:A});U.current={acceptedTypes:h,returnType:g,multiple:v,maxFiles:y,maxSize:b,allowDirectories:S,disabled:w,onFilesChanged:O,onReject:k,onStatusChanged:A};let W=r(e=>{B.current!==e&&(B.current=e,L(e),U.current.onStatusChanged?.(e))},[]),G=r(async e=>{let n=U.current,r=[],i=[];for(let t of e)d(t,n.acceptedTypes)?n.maxSize!=null&&t.size>n.maxSize?r.push({file:t,reason:`tooLarge`}):i.push(t):r.push({file:t,reason:`unacceptedType`});n.multiple||(i=i.slice(0,1)),n.maxFiles!=null&&i.length>n.maxFiles&&(i.slice(n.maxFiles).forEach(e=>r.push({file:e,reason:`tooMany`})),i=i.slice(0,n.maxFiles));let a=await Promise.all(i.map(e=>f(e,n.returnType))),o=[];a.forEach((e,t)=>{e.ok?o.push(e.value):r.push({file:i[t],reason:`readError`})}),r.length&&n.onReject?.(r),W(r.length&&!o.length?t.Error:t.Idle),o.length&&n.onFilesChanged?.(o)},[W]);i(()=>{let e=e=>!e||Array.from(e.types||[]).includes(`Files`),n=n=>{U.current.disabled||!e(n.dataTransfer)||(V.current+=1,(B.current===t.Idle||B.current===t.Error)&&W(t.Dragging))},r=()=>{V.current=Math.max(0,V.current-1),V.current===0&&B.current===t.Dragging&&W(t.Idle)},i=()=>{V.current=0,H.current=0,B.current===t.Dragging&&W(t.Idle)};return window.addEventListener(`dragenter`,n),window.addEventListener(`dragleave`,r),window.addEventListener(`drop`,i),()=>{window.removeEventListener(`dragenter`,n),window.removeEventListener(`dragleave`,r),window.removeEventListener(`drop`,i)}},[W]);let K=r(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&(e.dataTransfer.dropEffect=w?`none`:`copy`)},[w]),q=r(e=>{e.preventDefault(),e.stopPropagation(),!w&&(H.current+=1,W(t.Active))},[w,W]),J=r(e=>{e.preventDefault(),e.stopPropagation(),H.current=Math.max(0,H.current-1),H.current===0&&W(V.current>0?t.Dragging:t.Idle)},[W]),Y=r(e=>{if(e.preventDefault(),e.stopPropagation(),H.current=0,V.current=0,w||!e.dataTransfer){W(t.Idle);return}W(t.Idle),m(e.dataTransfer,U.current.allowDirectories).then(e=>{e.length&&G(e)})},[w,W,G]),X=r(()=>{w||!_||R.current?.click()},[w,_]),Z=r(e=>{let t=Array.from(e.target.files||[]);e.target.value=``,t.length&&G(t)},[G]),Q=r(e=>{if(!x||w)return;let t=Array.from(e.clipboardData?.files||[]);t.length&&(e.preventDefault(),G(t))},[x,w,G]),ee=r(e=>{!_||w||(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),X())},[_,w,X]),$=I===t.Dragging||I===t.Active,te={status:I,isDragging:$,isActive:I===t.Active,disabled:w},ne=h.length?h.join(`,`):void 0;return u(`div`,{...F,className:c(n.dropZone,_&&n.dropZoneClickable,w&&n.dropZoneDisabled,!T&&n.dropZoneDefault,!T&&I===t.Dragging&&n.dropZoneDragging,!T&&I===t.Active&&n.dropZoneActive,!T&&I===t.Error&&n.dropZoneError,N?.root,j),style:{...P?.root,...M},onDragEnter:q,onDragLeave:J,onDragOver:K,onDrop:Y,onClick:X,onKeyDown:ee,onPaste:x?Q:void 0,role:_?`button`:void 0,tabIndex:_&&!w?0:void 0,"aria-disabled":w||void 0,children:[l(`div`,{className:c(n.dropZoneContent,$&&n.dropZoneContentDragging,N?.content),style:P?.content,children:typeof p==`function`?p(te):p}),E&&D&&D.length>0&&l(`div`,{className:c(n.dropZoneFileList,N?.fileList),style:P?.fileList,children:D.map((e,t)=>l(`div`,{className:c(n.dropZoneFileItem,N?.fileItem),style:P?.fileItem,children:e.name},`${e.name}-${t}`))}),l(`input`,{ref:R,id:z,type:`file`,className:c(n.dropZoneInput,N?.input),style:P?.input,accept:ne,multiple:v,capture:C,disabled:w,onChange:Z,tabIndex:-1,"aria-hidden":`true`})]})}export{h as default};
|
|
1
|
+
import{DropZoneReturnType as e,DropZoneStatus as t}from"./dropZone.types.mjs";import n from"./dropZone.module.mjs";import{useCallback as r,useEffect as i,useId as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d(e,t){if(!t.length)return!0;let n=e.name.toLowerCase(),r=e.type.toLowerCase();return t.some(e=>{let t=e.trim().toLowerCase();return t?t.startsWith(`.`)?n.endsWith(t):t.endsWith(`/*`)?r.startsWith(t.slice(0,-1)):r===t:!1})}function f(t,n){return n===e.File?Promise.resolve({ok:!0,value:t}):new Promise(r=>{let i=new FileReader;i.onload=e=>{let n=e.target?.result;if(typeof n!=`string`){r({ok:!1});return}r({ok:!0,value:{name:t.name,size:t.size,type:t.type,lastModified:t.lastModified,data:n}})},i.onerror=()=>r({ok:!1}),n===e.Base64?i.readAsDataURL(t):i.readAsText(t)})}function p(e){return new Promise(t=>{if(e.isFile)e.file(e=>t([e]),()=>t([]));else if(e.isDirectory){let n=e.createReader(),r=[],i=()=>{n.readEntries(e=>{e.length?(r.push(...e),i()):Promise.all(r.map(p)).then(e=>t(e.flat()))},()=>t([]))};i()}else t([])})}function m(e,t){let n=e.items;if(n&&n.length){if(t&&typeof n[0].webkitGetAsEntry==`function`){let e=[];for(let t=0;t<n.length;t+=1)if(n[t].kind===`file`){let r=n[t].webkitGetAsEntry();r&&e.push(r)}if(e.length)return Promise.all(e.map(p)).then(e=>e.flat())}let e=[];for(let t=0;t<n.length;t+=1)if(n[t].kind===`file`){let r=n[t].getAsFile();r&&e.push(r)}return Promise.resolve(e)}return Promise.resolve(Array.from(e.files||[]))}function h({children:p,acceptedTypes:h=[],returnType:g=e.File,openPickerOnClick:_=!1,multiple:v=!0,maxFiles:y,maxSize:b,enablePaste:x=!1,allowDirectories:S=!0,capture:C,disabled:w=!1,unstyled:T=!1,showFileNames:E=!1,files:D,onFilesChanged:O,onReject:k,onStatusChanged:A,className:j,style:M,classNames:N,styles:P,...F}){let[I,L]=s(t.Idle),R=o(null),z=a(),B=o(I),V=o(0),H=o(0),U=o({acceptedTypes:h,returnType:g,multiple:v,maxFiles:y,maxSize:b,allowDirectories:S,disabled:w,onFilesChanged:O,onReject:k,onStatusChanged:A});i(()=>{U.current={acceptedTypes:h,returnType:g,multiple:v,maxFiles:y,maxSize:b,allowDirectories:S,disabled:w,onFilesChanged:O,onReject:k,onStatusChanged:A}});let W=r(e=>{B.current!==e&&(B.current=e,L(e),U.current.onStatusChanged?.(e))},[]),G=r(async e=>{let n=U.current,r=[],i=[];for(let t of e)d(t,n.acceptedTypes)?n.maxSize!=null&&t.size>n.maxSize?r.push({file:t,reason:`tooLarge`}):i.push(t):r.push({file:t,reason:`unacceptedType`});n.multiple||(i=i.slice(0,1)),n.maxFiles!=null&&i.length>n.maxFiles&&(i.slice(n.maxFiles).forEach(e=>r.push({file:e,reason:`tooMany`})),i=i.slice(0,n.maxFiles));let a=await Promise.all(i.map(e=>f(e,n.returnType))),o=[];a.forEach((e,t)=>{e.ok?o.push(e.value):r.push({file:i[t],reason:`readError`})}),r.length&&n.onReject?.(r),W(r.length&&!o.length?t.Error:t.Idle),o.length&&n.onFilesChanged?.(o)},[W]);i(()=>{let e=e=>!e||Array.from(e.types||[]).includes(`Files`),n=n=>{U.current.disabled||!e(n.dataTransfer)||(V.current+=1,(B.current===t.Idle||B.current===t.Error)&&W(t.Dragging))},r=()=>{V.current=Math.max(0,V.current-1),V.current===0&&B.current===t.Dragging&&W(t.Idle)},i=()=>{V.current=0,H.current=0,B.current===t.Dragging&&W(t.Idle)};return window.addEventListener(`dragenter`,n),window.addEventListener(`dragleave`,r),window.addEventListener(`drop`,i),()=>{window.removeEventListener(`dragenter`,n),window.removeEventListener(`dragleave`,r),window.removeEventListener(`drop`,i)}},[W]);let K=r(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&(e.dataTransfer.dropEffect=w?`none`:`copy`)},[w]),q=r(e=>{e.preventDefault(),e.stopPropagation(),!w&&(H.current+=1,W(t.Active))},[w,W]),J=r(e=>{e.preventDefault(),e.stopPropagation(),H.current=Math.max(0,H.current-1),H.current===0&&W(V.current>0?t.Dragging:t.Idle)},[W]),Y=r(e=>{if(e.preventDefault(),e.stopPropagation(),H.current=0,V.current=0,w||!e.dataTransfer){W(t.Idle);return}W(t.Idle),m(e.dataTransfer,U.current.allowDirectories).then(e=>{e.length&&G(e)})},[w,W,G]),X=r(()=>{w||!_||R.current?.click()},[w,_]),Z=r(e=>{let t=Array.from(e.target.files||[]);e.target.value=``,t.length&&G(t)},[G]),Q=r(e=>{if(!x||w)return;let t=Array.from(e.clipboardData?.files||[]);t.length&&(e.preventDefault(),G(t))},[x,w,G]),ee=r(e=>{!_||w||(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),X())},[_,w,X]),$=I===t.Dragging||I===t.Active,te={status:I,isDragging:$,isActive:I===t.Active,disabled:w},ne=h.length?h.join(`,`):void 0;return u(`div`,{...F,className:c(n.dropZone,_&&n.dropZoneClickable,w&&n.dropZoneDisabled,!T&&n.dropZoneDefault,!T&&I===t.Dragging&&n.dropZoneDragging,!T&&I===t.Active&&n.dropZoneActive,!T&&I===t.Error&&n.dropZoneError,N?.root,j),style:{...P?.root,...M},onDragEnter:q,onDragLeave:J,onDragOver:K,onDrop:Y,onClick:X,onKeyDown:ee,onPaste:x?Q:void 0,role:_?`button`:void 0,tabIndex:_&&!w?0:void 0,"aria-disabled":w||void 0,children:[l(`div`,{className:c(n.dropZoneContent,$&&n.dropZoneContentDragging,N?.content),style:P?.content,children:typeof p==`function`?p(te):p}),E&&D&&D.length>0&&l(`div`,{className:c(n.dropZoneFileList,N?.fileList),style:P?.fileList,children:D.map((e,t)=>l(`div`,{className:c(n.dropZoneFileItem,N?.fileItem),style:P?.fileItem,children:e.name},`${e.name}-${t}`))}),l(`input`,{ref:R,id:z,type:`file`,className:c(n.dropZoneInput,N?.input),style:P?.input,accept:ne,multiple:v,capture:C,disabled:w,onChange:Z,tabIndex:-1,"aria-hidden":`true`})]})}export{h as default};
|
|
2
2
|
//# sourceMappingURL=dropZone.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropZone.mjs","names":[],"sources":["../../../../package/common/dropZone/dropZone.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport {\n DropZoneProps,\n DropZoneReturnType,\n DropZoneStatus,\n DropZoneRejection,\n DropZoneRenderState,\n LoadedFile,\n} from './dropZone.types';\n\nimport styles from './dropZone.module.pcss';\n\n// ── Pure helpers ──────────────────────────────────────────────────────────────\n\n/** True when a file matches one of the accepted tokens (extension, mime, or wildcard). */\nfunction matchesAccepted(file: File, acceptedTypes: string[]): boolean {\n if (!acceptedTypes.length) return true;\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return acceptedTypes.some((raw) => {\n const accept = raw.trim().toLowerCase();\n if (!accept) return false;\n if (accept.startsWith('.')) return name.endsWith(accept); // extension\n if (accept.endsWith('/*')) return type.startsWith(accept.slice(0, -1)); // wildcard e.g. image/\n return type === accept; // exact mime type\n });\n}\n\ntype ReadResult = { ok: true; value: File | LoadedFile } | { ok: false };\n\n/** Read a single file into the requested return shape. Never rejects — failures resolve to `{ ok: false }`. */\nfunction readFile(file: File, returnType: DropZoneReturnType): Promise<ReadResult> {\n if (returnType === DropZoneReturnType.File) {\n return Promise.resolve({ ok: true, value: file });\n }\n return new Promise<ReadResult>((resolve) => {\n const reader = new FileReader();\n reader.onload = (event) => {\n const data = event.target?.result;\n if (typeof data !== 'string') {\n resolve({ ok: false });\n return;\n }\n resolve({\n ok: true,\n value: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified, data },\n });\n };\n reader.onerror = () => resolve({ ok: false });\n if (returnType === DropZoneReturnType.Base64) reader.readAsDataURL(file);\n else reader.readAsText(file);\n });\n}\n\n/** Recursively flatten a dropped file-system entry into a list of Files. */\nfunction readEntry(entry: FileSystemEntry): Promise<File[]> {\n return new Promise((resolve) => {\n if (entry.isFile) {\n (entry as FileSystemFileEntry).file(\n (file) => resolve([file]),\n () => resolve([])\n );\n } else if (entry.isDirectory) {\n const reader = (entry as FileSystemDirectoryEntry).createReader();\n const collected: FileSystemEntry[] = [];\n const readBatch = () => {\n // readEntries yields entries in chunks — keep calling until it returns empty.\n reader.readEntries((batch) => {\n if (!batch.length) {\n Promise.all(collected.map(readEntry)).then((nested) => resolve(nested.flat()));\n } else {\n collected.push(...batch);\n readBatch();\n }\n }, () => resolve([]));\n };\n readBatch();\n } else {\n resolve([]);\n }\n });\n}\n\n/**\n * Extract Files from a drop's DataTransfer. `webkitGetAsEntry()` must be called\n * synchronously within the drop handler, so all entries are captured before any await.\n */\nfunction gatherFiles(dataTransfer: DataTransfer, allowDirectories: boolean): Promise<File[]> {\n const items = dataTransfer.items;\n if (items && items.length) {\n if (allowDirectories && typeof items[0].webkitGetAsEntry === 'function') {\n const entries: FileSystemEntry[] = [];\n for (let i = 0; i < items.length; i += 1) {\n if (items[i].kind === 'file') {\n const entry = items[i].webkitGetAsEntry();\n if (entry) entries.push(entry);\n }\n }\n if (entries.length) {\n return Promise.all(entries.map(readEntry)).then((nested) => nested.flat());\n }\n }\n const files: File[] = [];\n for (let i = 0; i < items.length; i += 1) {\n if (items[i].kind === 'file') {\n const file = items[i].getAsFile();\n if (file) files.push(file);\n }\n }\n return Promise.resolve(files);\n }\n return Promise.resolve(Array.from(dataTransfer.files || []));\n}\n\n// ── Component ───────────────────────────────────────────────────────────────\n\nfunction DropZone({\n children,\n acceptedTypes = [],\n returnType = DropZoneReturnType.File,\n openPickerOnClick = false,\n multiple = true,\n maxFiles,\n maxSize,\n enablePaste = false,\n allowDirectories = true,\n capture,\n disabled = false,\n unstyled = false,\n showFileNames = false,\n files,\n onFilesChanged,\n onReject,\n onStatusChanged,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: DropZoneProps) {\n const [status, setStatus] = useState<DropZoneStatus>(DropZoneStatus.Idle);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const inputId = useId();\n const statusRef = useRef(status);\n const windowDragDepth = useRef(0);\n const zoneDragDepth = useRef(0);\n\n // Latest props/callbacks, so the once-mounted window listeners never read stale values.\n const cfg = useRef({\n acceptedTypes, returnType, multiple, maxFiles, maxSize, allowDirectories,\n disabled, onFilesChanged, onReject, onStatusChanged,\n });\n cfg.current = {\n acceptedTypes, returnType, multiple, maxFiles, maxSize, allowDirectories,\n disabled, onFilesChanged, onReject, onStatusChanged,\n };\n\n const changeStatus = useCallback((next: DropZoneStatus) => {\n if (statusRef.current === next) return;\n statusRef.current = next;\n setStatus(next);\n cfg.current.onStatusChanged?.(next);\n }, []);\n\n const processFiles = useCallback(async (incoming: File[]) => {\n const c = cfg.current;\n const rejections: DropZoneRejection[] = [];\n let accepted: File[] = [];\n\n for (const file of incoming) {\n if (!matchesAccepted(file, c.acceptedTypes)) {\n rejections.push({ file, reason: 'unacceptedType' });\n } else if (c.maxSize != null && file.size > c.maxSize) {\n rejections.push({ file, reason: 'tooLarge' });\n } else {\n accepted.push(file);\n }\n }\n\n if (!c.multiple) accepted = accepted.slice(0, 1);\n if (c.maxFiles != null && accepted.length > c.maxFiles) {\n accepted.slice(c.maxFiles).forEach((file) => rejections.push({ file, reason: 'tooMany' }));\n accepted = accepted.slice(0, c.maxFiles);\n }\n\n // Read in input order — Promise.all preserves index regardless of completion order.\n const results = await Promise.all(accepted.map((file) => readFile(file, c.returnType)));\n const loaded: (File | LoadedFile)[] = [];\n results.forEach((result, index) => {\n if (result.ok) loaded.push(result.value);\n else rejections.push({ file: accepted[index], reason: 'readError' });\n });\n\n if (rejections.length) c.onReject?.(rejections);\n changeStatus(rejections.length && !loaded.length ? DropZoneStatus.Error : DropZoneStatus.Idle);\n if (loaded.length) c.onFilesChanged?.(loaded);\n }, [changeStatus]);\n\n // Window-level drag tracking via an enter/leave depth counter (robust against bubbling).\n useEffect(() => {\n const hasFiles = (dt: DataTransfer | null) =>\n !dt || Array.from(dt.types || []).includes('Files');\n\n const onWindowDragEnter = (e: DragEvent) => {\n if (cfg.current.disabled || !hasFiles(e.dataTransfer)) return;\n windowDragDepth.current += 1;\n if (statusRef.current === DropZoneStatus.Idle || statusRef.current === DropZoneStatus.Error) {\n changeStatus(DropZoneStatus.Dragging);\n }\n };\n const onWindowDragLeave = () => {\n windowDragDepth.current = Math.max(0, windowDragDepth.current - 1);\n if (windowDragDepth.current === 0 && statusRef.current === DropZoneStatus.Dragging) {\n changeStatus(DropZoneStatus.Idle);\n }\n };\n const onWindowDrop = () => {\n windowDragDepth.current = 0;\n zoneDragDepth.current = 0;\n if (statusRef.current === DropZoneStatus.Dragging) changeStatus(DropZoneStatus.Idle);\n };\n\n window.addEventListener('dragenter', onWindowDragEnter);\n window.addEventListener('dragleave', onWindowDragLeave);\n window.addEventListener('drop', onWindowDrop);\n return () => {\n window.removeEventListener('dragenter', onWindowDragEnter);\n window.removeEventListener('dragleave', onWindowDragLeave);\n window.removeEventListener('drop', onWindowDrop);\n };\n }, [changeStatus]);\n\n const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.dataTransfer) e.dataTransfer.dropEffect = disabled ? 'none' : 'copy';\n }, [disabled]);\n\n const onDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n zoneDragDepth.current += 1;\n changeStatus(DropZoneStatus.Active);\n }, [disabled, changeStatus]);\n\n const onDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n zoneDragDepth.current = Math.max(0, zoneDragDepth.current - 1);\n if (zoneDragDepth.current === 0) {\n // Left the zone, but a drag may still be active elsewhere in the window.\n changeStatus(windowDragDepth.current > 0 ? DropZoneStatus.Dragging : DropZoneStatus.Idle);\n }\n }, [changeStatus]);\n\n const onDrop = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n zoneDragDepth.current = 0;\n windowDragDepth.current = 0;\n if (disabled || !e.dataTransfer) {\n changeStatus(DropZoneStatus.Idle);\n return;\n }\n changeStatus(DropZoneStatus.Idle);\n // gatherFiles captures any directory entries synchronously before awaiting.\n gatherFiles(e.dataTransfer, cfg.current.allowDirectories).then((collected) => {\n if (collected.length) processFiles(collected);\n });\n }, [disabled, changeStatus, processFiles]);\n\n const openPicker = useCallback(() => {\n if (disabled || !openPickerOnClick) return;\n inputRef.current?.click();\n }, [disabled, openPickerOnClick]);\n\n const onInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const selected = Array.from(e.target.files || []);\n // Reset so picking the same file again still fires onChange.\n e.target.value = '';\n if (selected.length) processFiles(selected);\n }, [processFiles]);\n\n const onPaste = useCallback((e: React.ClipboardEvent<HTMLDivElement>) => {\n if (!enablePaste || disabled) return;\n const pasted = Array.from(e.clipboardData?.files || []);\n if (pasted.length) {\n e.preventDefault();\n processFiles(pasted);\n }\n }, [enablePaste, disabled, processFiles]);\n\n const onKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n if (!openPickerOnClick || disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n openPicker();\n }\n }, [openPickerOnClick, disabled, openPicker]);\n\n const isDragging = status === DropZoneStatus.Dragging || status === DropZoneStatus.Active;\n const renderState: DropZoneRenderState = {\n status,\n isDragging,\n isActive: status === DropZoneStatus.Active,\n disabled,\n };\n\n const acceptAttr = acceptedTypes.length ? acceptedTypes.join(',') : undefined;\n\n return (\n <div\n {...rest}\n className={cx(\n styles.dropZone,\n openPickerOnClick && styles.dropZoneClickable,\n disabled && styles.dropZoneDisabled,\n !unstyled && styles.dropZoneDefault,\n !unstyled && status === DropZoneStatus.Dragging && styles.dropZoneDragging,\n !unstyled && status === DropZoneStatus.Active && styles.dropZoneActive,\n !unstyled && status === DropZoneStatus.Error && styles.dropZoneError,\n classNames?.root,\n className\n )}\n style={{ ...slotStyles?.root, ...style }}\n onDragEnter={onDragEnter}\n onDragLeave={onDragLeave}\n onDragOver={onDragOver}\n onDrop={onDrop}\n onClick={openPicker}\n onKeyDown={onKeyDown}\n onPaste={enablePaste ? onPaste : undefined}\n role={openPickerOnClick ? 'button' : undefined}\n tabIndex={openPickerOnClick && !disabled ? 0 : undefined}\n aria-disabled={disabled || undefined}\n >\n <div\n className={cx(styles.dropZoneContent, isDragging && styles.dropZoneContentDragging, classNames?.content)}\n style={slotStyles?.content}\n >\n {typeof children === 'function' ? children(renderState) : children}\n </div>\n\n {showFileNames && files && files.length > 0 && (\n <div className={cx(styles.dropZoneFileList, classNames?.fileList)} style={slotStyles?.fileList}>\n {files.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className={cx(styles.dropZoneFileItem, classNames?.fileItem)}\n style={slotStyles?.fileItem}\n >\n {file.name}\n </div>\n ))}\n </div>\n )}\n\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n className={cx(styles.dropZoneInput, classNames?.input)}\n style={slotStyles?.input}\n accept={acceptAttr}\n multiple={multiple}\n capture={capture}\n disabled={disabled}\n onChange={onInputChange}\n tabIndex={-1}\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n\nexport default DropZone;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropZone.mjs","names":[],"sources":["../../../../package/common/dropZone/dropZone.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport {\n DropZoneProps,\n DropZoneReturnType,\n DropZoneStatus,\n DropZoneRejection,\n DropZoneRenderState,\n LoadedFile,\n} from './dropZone.types';\n\nimport styles from './dropZone.module.pcss';\n\n// ── Pure helpers ──────────────────────────────────────────────────────────────\n\n/** True when a file matches one of the accepted tokens (extension, mime, or wildcard). */\nfunction matchesAccepted(file: File, acceptedTypes: string[]): boolean {\n if (!acceptedTypes.length) return true;\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return acceptedTypes.some((raw) => {\n const accept = raw.trim().toLowerCase();\n if (!accept) return false;\n if (accept.startsWith('.')) return name.endsWith(accept); // extension\n if (accept.endsWith('/*')) return type.startsWith(accept.slice(0, -1)); // wildcard e.g. image/\n return type === accept; // exact mime type\n });\n}\n\ntype ReadResult = { ok: true; value: File | LoadedFile } | { ok: false };\n\n/** Read a single file into the requested return shape. Never rejects — failures resolve to `{ ok: false }`. */\nfunction readFile(file: File, returnType: DropZoneReturnType): Promise<ReadResult> {\n if (returnType === DropZoneReturnType.File) {\n return Promise.resolve({ ok: true, value: file });\n }\n return new Promise<ReadResult>((resolve) => {\n const reader = new FileReader();\n reader.onload = (event) => {\n const data = event.target?.result;\n if (typeof data !== 'string') {\n resolve({ ok: false });\n return;\n }\n resolve({\n ok: true,\n value: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified, data },\n });\n };\n reader.onerror = () => resolve({ ok: false });\n if (returnType === DropZoneReturnType.Base64) reader.readAsDataURL(file);\n else reader.readAsText(file);\n });\n}\n\n/** Recursively flatten a dropped file-system entry into a list of Files. */\nfunction readEntry(entry: FileSystemEntry): Promise<File[]> {\n return new Promise((resolve) => {\n if (entry.isFile) {\n (entry as FileSystemFileEntry).file(\n (file) => resolve([file]),\n () => resolve([])\n );\n } else if (entry.isDirectory) {\n const reader = (entry as FileSystemDirectoryEntry).createReader();\n const collected: FileSystemEntry[] = [];\n const readBatch = () => {\n // readEntries yields entries in chunks — keep calling until it returns empty.\n reader.readEntries((batch) => {\n if (!batch.length) {\n Promise.all(collected.map(readEntry)).then((nested) => resolve(nested.flat()));\n } else {\n collected.push(...batch);\n readBatch();\n }\n }, () => resolve([]));\n };\n readBatch();\n } else {\n resolve([]);\n }\n });\n}\n\n/**\n * Extract Files from a drop's DataTransfer. `webkitGetAsEntry()` must be called\n * synchronously within the drop handler, so all entries are captured before any await.\n */\nfunction gatherFiles(dataTransfer: DataTransfer, allowDirectories: boolean): Promise<File[]> {\n const items = dataTransfer.items;\n if (items && items.length) {\n if (allowDirectories && typeof items[0].webkitGetAsEntry === 'function') {\n const entries: FileSystemEntry[] = [];\n for (let i = 0; i < items.length; i += 1) {\n if (items[i].kind === 'file') {\n const entry = items[i].webkitGetAsEntry();\n if (entry) entries.push(entry);\n }\n }\n if (entries.length) {\n return Promise.all(entries.map(readEntry)).then((nested) => nested.flat());\n }\n }\n const files: File[] = [];\n for (let i = 0; i < items.length; i += 1) {\n if (items[i].kind === 'file') {\n const file = items[i].getAsFile();\n if (file) files.push(file);\n }\n }\n return Promise.resolve(files);\n }\n return Promise.resolve(Array.from(dataTransfer.files || []));\n}\n\n// ── Component ───────────────────────────────────────────────────────────────\n\nfunction DropZone({\n children,\n acceptedTypes = [],\n returnType = DropZoneReturnType.File,\n openPickerOnClick = false,\n multiple = true,\n maxFiles,\n maxSize,\n enablePaste = false,\n allowDirectories = true,\n capture,\n disabled = false,\n unstyled = false,\n showFileNames = false,\n files,\n onFilesChanged,\n onReject,\n onStatusChanged,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: DropZoneProps) {\n const [status, setStatus] = useState<DropZoneStatus>(DropZoneStatus.Idle);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const inputId = useId();\n const statusRef = useRef(status);\n const windowDragDepth = useRef(0);\n const zoneDragDepth = useRef(0);\n\n // Latest props/callbacks, so the once-mounted window listeners never read stale values.\n const cfg = useRef({\n acceptedTypes, returnType, multiple, maxFiles, maxSize, allowDirectories,\n disabled, onFilesChanged, onReject, onStatusChanged,\n });\n useEffect(() => {\n cfg.current = {\n acceptedTypes, returnType, multiple, maxFiles, maxSize, allowDirectories,\n disabled, onFilesChanged, onReject, onStatusChanged,\n };\n });\n\n const changeStatus = useCallback((next: DropZoneStatus) => {\n if (statusRef.current === next) return;\n statusRef.current = next;\n setStatus(next);\n cfg.current.onStatusChanged?.(next);\n }, []);\n\n const processFiles = useCallback(async (incoming: File[]) => {\n const c = cfg.current;\n const rejections: DropZoneRejection[] = [];\n let accepted: File[] = [];\n\n for (const file of incoming) {\n if (!matchesAccepted(file, c.acceptedTypes)) {\n rejections.push({ file, reason: 'unacceptedType' });\n } else if (c.maxSize != null && file.size > c.maxSize) {\n rejections.push({ file, reason: 'tooLarge' });\n } else {\n accepted.push(file);\n }\n }\n\n if (!c.multiple) accepted = accepted.slice(0, 1);\n if (c.maxFiles != null && accepted.length > c.maxFiles) {\n accepted.slice(c.maxFiles).forEach((file) => rejections.push({ file, reason: 'tooMany' }));\n accepted = accepted.slice(0, c.maxFiles);\n }\n\n // Read in input order — Promise.all preserves index regardless of completion order.\n const results = await Promise.all(accepted.map((file) => readFile(file, c.returnType)));\n const loaded: (File | LoadedFile)[] = [];\n results.forEach((result, index) => {\n if (result.ok) loaded.push(result.value);\n else rejections.push({ file: accepted[index], reason: 'readError' });\n });\n\n if (rejections.length) c.onReject?.(rejections);\n changeStatus(rejections.length && !loaded.length ? DropZoneStatus.Error : DropZoneStatus.Idle);\n if (loaded.length) c.onFilesChanged?.(loaded);\n }, [changeStatus]);\n\n // Window-level drag tracking via an enter/leave depth counter (robust against bubbling).\n useEffect(() => {\n const hasFiles = (dt: DataTransfer | null) =>\n !dt || Array.from(dt.types || []).includes('Files');\n\n const onWindowDragEnter = (e: DragEvent) => {\n if (cfg.current.disabled || !hasFiles(e.dataTransfer)) return;\n windowDragDepth.current += 1;\n if (statusRef.current === DropZoneStatus.Idle || statusRef.current === DropZoneStatus.Error) {\n changeStatus(DropZoneStatus.Dragging);\n }\n };\n const onWindowDragLeave = () => {\n windowDragDepth.current = Math.max(0, windowDragDepth.current - 1);\n if (windowDragDepth.current === 0 && statusRef.current === DropZoneStatus.Dragging) {\n changeStatus(DropZoneStatus.Idle);\n }\n };\n const onWindowDrop = () => {\n windowDragDepth.current = 0;\n zoneDragDepth.current = 0;\n if (statusRef.current === DropZoneStatus.Dragging) changeStatus(DropZoneStatus.Idle);\n };\n\n window.addEventListener('dragenter', onWindowDragEnter);\n window.addEventListener('dragleave', onWindowDragLeave);\n window.addEventListener('drop', onWindowDrop);\n return () => {\n window.removeEventListener('dragenter', onWindowDragEnter);\n window.removeEventListener('dragleave', onWindowDragLeave);\n window.removeEventListener('drop', onWindowDrop);\n };\n }, [changeStatus]);\n\n const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.dataTransfer) e.dataTransfer.dropEffect = disabled ? 'none' : 'copy';\n }, [disabled]);\n\n const onDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n if (disabled) return;\n zoneDragDepth.current += 1;\n changeStatus(DropZoneStatus.Active);\n }, [disabled, changeStatus]);\n\n const onDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n zoneDragDepth.current = Math.max(0, zoneDragDepth.current - 1);\n if (zoneDragDepth.current === 0) {\n // Left the zone, but a drag may still be active elsewhere in the window.\n changeStatus(windowDragDepth.current > 0 ? DropZoneStatus.Dragging : DropZoneStatus.Idle);\n }\n }, [changeStatus]);\n\n const onDrop = useCallback((e: React.DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n zoneDragDepth.current = 0;\n windowDragDepth.current = 0;\n if (disabled || !e.dataTransfer) {\n changeStatus(DropZoneStatus.Idle);\n return;\n }\n changeStatus(DropZoneStatus.Idle);\n // gatherFiles captures any directory entries synchronously before awaiting.\n gatherFiles(e.dataTransfer, cfg.current.allowDirectories).then((collected) => {\n if (collected.length) processFiles(collected);\n });\n }, [disabled, changeStatus, processFiles]);\n\n const openPicker = useCallback(() => {\n if (disabled || !openPickerOnClick) return;\n inputRef.current?.click();\n }, [disabled, openPickerOnClick]);\n\n const onInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const selected = Array.from(e.target.files || []);\n // Reset so picking the same file again still fires onChange.\n e.target.value = '';\n if (selected.length) processFiles(selected);\n }, [processFiles]);\n\n const onPaste = useCallback((e: React.ClipboardEvent<HTMLDivElement>) => {\n if (!enablePaste || disabled) return;\n const pasted = Array.from(e.clipboardData?.files || []);\n if (pasted.length) {\n e.preventDefault();\n processFiles(pasted);\n }\n }, [enablePaste, disabled, processFiles]);\n\n const onKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n if (!openPickerOnClick || disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n openPicker();\n }\n }, [openPickerOnClick, disabled, openPicker]);\n\n const isDragging = status === DropZoneStatus.Dragging || status === DropZoneStatus.Active;\n const renderState: DropZoneRenderState = {\n status,\n isDragging,\n isActive: status === DropZoneStatus.Active,\n disabled,\n };\n\n const acceptAttr = acceptedTypes.length ? acceptedTypes.join(',') : undefined;\n\n return (\n <div\n {...rest}\n className={cx(\n styles.dropZone,\n openPickerOnClick && styles.dropZoneClickable,\n disabled && styles.dropZoneDisabled,\n !unstyled && styles.dropZoneDefault,\n !unstyled && status === DropZoneStatus.Dragging && styles.dropZoneDragging,\n !unstyled && status === DropZoneStatus.Active && styles.dropZoneActive,\n !unstyled && status === DropZoneStatus.Error && styles.dropZoneError,\n classNames?.root,\n className\n )}\n style={{ ...slotStyles?.root, ...style }}\n onDragEnter={onDragEnter}\n onDragLeave={onDragLeave}\n onDragOver={onDragOver}\n onDrop={onDrop}\n onClick={openPicker}\n onKeyDown={onKeyDown}\n onPaste={enablePaste ? onPaste : undefined}\n role={openPickerOnClick ? 'button' : undefined}\n tabIndex={openPickerOnClick && !disabled ? 0 : undefined}\n aria-disabled={disabled || undefined}\n >\n <div\n className={cx(styles.dropZoneContent, isDragging && styles.dropZoneContentDragging, classNames?.content)}\n style={slotStyles?.content}\n >\n {typeof children === 'function' ? children(renderState) : children}\n </div>\n\n {showFileNames && files && files.length > 0 && (\n <div className={cx(styles.dropZoneFileList, classNames?.fileList)} style={slotStyles?.fileList}>\n {files.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className={cx(styles.dropZoneFileItem, classNames?.fileItem)}\n style={slotStyles?.fileItem}\n >\n {file.name}\n </div>\n ))}\n </div>\n )}\n\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n className={cx(styles.dropZoneInput, classNames?.input)}\n style={slotStyles?.input}\n accept={acceptAttr}\n multiple={multiple}\n capture={capture}\n disabled={disabled}\n onChange={onInputChange}\n tabIndex={-1}\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n\nexport default DropZone;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{isHiddenByAnyAncestor as e}from"../utils/scrollAncestors.mjs";import{getVisualViewportRect as t}from"../utils/visualViewport.mjs";import{useAnchorTracking as n}from"../hooks/useAnchorTracking.mjs";var r=8;function i({isOpen:i,triggerRef:a,anchorRef:o,panelRef:s,flipClassName:c,placement:l=`bottom`}){n({isOpen:i,triggerRef:a,elementRefs:[o],resizeRefs:[o,s],deps:[o,s,c,l],update:(n,i)=>{let a=o.current;if(!a)return;let u=n.getBoundingClientRect(),d=s.current?.offsetHeight??0,f=t(),p=f.top+f.height-u.bottom,m=u.top-f.top,h=l===`top`?m:p,g=h<d&&(l===`top`?p:m)>h,_=l===`top`?!g:g;a.classList.toggle(c,g),a.style.top=`${_?u.top-a.offsetHeight:u.bottom}px`,a.style.left=`${u.left}px`;let v=s.current;if(v){let e=v===a?0:v.offsetLeft,t=u.left+e,n=t+v.offsetWidth-(f.left+f.width-r),i=f.left+r-t;n>0?a.style.left=`${u.left-n}px`:i>0&&(a.style.left=`${u.left+i}px`)}a.style.visibility=e(u,i)?`hidden`:``},teardown:()=>{let e=o.current;e&&(e.style.top=``,e.style.left=``,e.style.visibility=``,e.classList.remove(c))}})}export{i as useFloatingPosition};
|
|
2
2
|
//# sourceMappingURL=useFloatingPosition.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFloatingPosition.mjs","names":[],"sources":["../../../../package/common/floatingMenu/useFloatingPosition.ts"],"sourcesContent":["import { useLayoutEffect, type RefObject } from 'react';\nimport { getScrollableAncestors, isHiddenByAnyAncestor } from 'components/common/utils/scrollAncestors';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\n\nexport interface UseFloatingPositionArgs {\n isOpen: boolean;\n /** The element the popup is anchored to. */\n triggerRef: RefObject<HTMLElement | null>;\n /** The `position: fixed` element that gets `top`/`left`/`visibility` written to it directly. */\n anchorRef: RefObject<HTMLElement | null>;\n /** The visible panel whose height decides whether there's room to render on the preferred side. */\n panelRef: RefObject<HTMLElement | null>;\n /** Class toggled on `anchorRef` when flipped to the non-preferred side. */\n flipClassName: string;\n /** Preferred side relative to the trigger (default `'bottom'`) — flips to the other side when there's no room. */\n placement?: 'top' | 'bottom';\n}\n\n/** Minimum gap kept between the panel and the viewport edge when horizontally clamped. */\nconst EDGE_MARGIN = 8;\n\n/**\n * Keeps a portaled, `position: fixed` popup anchored to a trigger element: tracks the\n * trigger across scroll/resize on every real scrolling ancestor, flips above the trigger\n * when there's no room below, and hides (not closes) the popup when the trigger itself\n * scrolls behind a clipping ancestor — matching Floating UI's autoUpdate + hide middleware.\n * It also shifts the popup horizontally to stay within the viewport: `align`/RTL-style CSS\n * only positions the panel relative to the anchor's own box and has no awareness of where\n * that box actually sits on screen, so a trigger near a screen edge would otherwise let the\n * panel overflow it with no correction.\n *\n * Position/visibility are written straight to the DOM via refs, not through React state:\n * going through setState -> re-render -> commit adds a round-trip that lags behind the\n * browser's own scroll painting by at least a frame.\n */\nexport function useFloatingPosition({ isOpen, triggerRef, anchorRef, panelRef, flipClassName, placement = 'bottom' }: UseFloatingPositionArgs) {\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let rafId: number | undefined;\n let teardown: (() => void) | undefined;\n\n // The anchor/trigger may not be mounted yet on the very first invocation of this\n // effect: a portal target (e.g. BodyEnd's `#bodyEnd` div) can still be missing on\n // the initial render, in which case the portaled content — and this ref — commits\n // one render later. Since refs never change identity, that later commit doesn't\n // re-trigger this effect on its own, so retry on the next frame until it's there.\n function trySetup() {\n const trigger = triggerRef.current;\n const anchor = anchorRef.current;\n if (!trigger || !anchor) {\n rafId = requestAnimationFrame(trySetup);\n return;\n }\n const ancestors = getScrollableAncestors(trigger);\n\n function updatePos() {\n const rect = trigger!.getBoundingClientRect();\n const panelHeight = panelRef.current?.offsetHeight ?? 0;\n // The visible area, not the full layout viewport — shrinks on mobile when a keyboard is\n // open, so flip/clamp react to what's actually on screen instead of the keyboard hiding\n // a panel that \"fit\" against the unshrunk layout viewport.\n const vv = getVisualViewportRect();\n const spaceBelow = vv.top + vv.height - rect.bottom;\n const spaceAbove = rect.top - vv.top;\n const preferredSpace = placement === 'top' ? spaceAbove : spaceBelow;\n const alternateSpace = placement === 'top' ? spaceBelow : spaceAbove;\n const flip = preferredSpace < panelHeight && alternateSpace > preferredSpace;\n const renderAbove = placement === 'top' ? !flip : flip;\n anchor!.classList.toggle(flipClassName, flip);\n // Shift up by the anchor's own height when rendering above, so this works whether\n // the anchor is a zero-height decorative wrapper (FloatingMenu, whose real panel\n // hangs off it via absolute-positioned CSS) or the visible panel itself\n // (Dropdown) — for the former the shift is a no-op (height 0).\n anchor!.style.top = `${renderAbove ? rect.top - anchor!.offsetHeight : rect.bottom}px`;\n anchor!.style.left = `${rect.left}px`;\n\n // Horizontal clamp: shift the anchor so the panel stays within the viewport, since\n // `align` CSS only positions it relative to the anchor's own box. Use offsetWidth/\n // offsetLeft (untransformed layout geometry), not getBoundingClientRect — both this\n // anchor and Dropdown's animate in via a CSS `transform: scale(...)`, which\n // getBoundingClientRect reflects but the box-model offsets don't, so measuring via\n // the transformed rect would under- or over-correct while that entrance animation runs.\n const panelForClamp = panelRef.current;\n if (panelForClamp) {\n const relativeLeft = panelForClamp === anchor ? 0 : panelForClamp.offsetLeft;\n const panelLeft = rect.left + relativeLeft;\n const panelRight = panelLeft + panelForClamp.offsetWidth;\n const overflowRight = panelRight - (vv.left + vv.width - EDGE_MARGIN);\n const overflowLeft = vv.left + EDGE_MARGIN - panelLeft;\n if (overflowRight > 0) {\n anchor!.style.left = `${rect.left - overflowRight}px`;\n } else if (overflowLeft > 0) {\n anchor!.style.left = `${rect.left + overflowLeft}px`;\n }\n }\n\n anchor!.style.visibility = isHiddenByAnyAncestor(rect, ancestors) ? 'hidden' : '';\n // The panel can mount a commit later than the anchor; once it's observed this\n // is a single Set lookup, so it costs nothing on the scroll path.\n const panel = panelRef.current;\n if (panel && !observed.has(panel)) observeSizeTargets();\n }\n\n // The panel's own height feeds both the flip decision and, when rendering above,\n // the top offset — so a popup whose content changes size while open (a filtered\n // list shrinking, an async list loading in) has to be re-measured, or it keeps a\n // top computed for its old height and ends up detached from the trigger.\n const resizeObserver = new ResizeObserver(() => updatePos());\n const observed = new Set<Element>();\n function observeSizeTargets() {\n // Both, since they're the same element for a panel-as-anchor consumer (Dropdown)\n // but not for a zero-height wrapper whose content sits inside it (FloatingMenu).\n // Called from updatePos too: the panel can mount a commit later than the anchor.\n [anchor, panelRef.current].forEach((el) => {\n if (!el || observed.has(el)) return;\n observed.add(el);\n resizeObserver.observe(el);\n });\n }\n\n updatePos();\n\n const scrollTargets: (Window | HTMLElement)[] = [window, ...ancestors];\n scrollTargets.forEach((t) => t.addEventListener('scroll', updatePos, { passive: true }));\n window.addEventListener('resize', updatePos);\n // `window.resize` doesn't reliably fire when a mobile keyboard opens/closes — visualViewport\n // does, and is what the space/clamp math above is measured against.\n window.visualViewport?.addEventListener('resize', updatePos);\n window.visualViewport?.addEventListener('scroll', updatePos);\n teardown = () => {\n scrollTargets.forEach((t) => t.removeEventListener('scroll', updatePos));\n window.removeEventListener('resize', updatePos);\n window.visualViewport?.removeEventListener('resize', updatePos);\n window.visualViewport?.removeEventListener('scroll', updatePos);\n resizeObserver.disconnect();\n // Positioning is written as inline styles, which outrank any class the popup might switch\n // to while it stays mounted — a popup turning into a bottom sheet (`useSheet`) would keep\n // the coordinates computed for its anchored self. Hand the element back as we found it.\n anchor!.style.top = '';\n anchor!.style.left = '';\n anchor!.style.visibility = '';\n anchor!.classList.remove(flipClassName);\n };\n }\n\n trySetup();\n\n return () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n teardown?.();\n };\n }, [isOpen, triggerRef, anchorRef, panelRef, flipClassName, placement]);\n}"],"mappings":"6MAmBA,IAAM,EAAc,EAgBpB,SAAgB,EAAoB,CAAE,SAAQ,aAAY,YAAW,WAAU,gBAAe,YAAY,UAAqC,CAC7I,MAAsB,CACpB,GAAI,CAAC,EAAQ,OAEb,IAAI,EACA,EAOJ,SAAS,GAAW,CAClB,IAAM,EAAU,EAAW,QACrB,EAAS,EAAU,QACzB,GAAI,CAAC,GAAW,CAAC,EAAQ,CACvB,EAAQ,sBAAsB,CAAQ,EACtC,MACF,CACA,IAAM,EAAY,EAAuB,CAAO,EAEhD,SAAS,GAAY,CACnB,IAAM,EAAO,EAAS,sBAAsB,EACtC,EAAc,EAAS,SAAS,cAAgB,EAIhD,EAAK,EAAsB,EAC3B,EAAa,EAAG,IAAM,EAAG,OAAS,EAAK,OACvC,EAAa,EAAK,IAAM,EAAG,IAC3B,EAAiB,IAAc,MAAQ,EAAa,EAEpD,EAAO,EAAiB,IADP,IAAc,MAAQ,EAAa,GACI,EACxD,EAAc,IAAc,MAAQ,CAAC,EAAO,EAClD,EAAQ,UAAU,OAAO,EAAe,CAAI,EAK5C,EAAQ,MAAM,IAAM,GAAG,EAAc,EAAK,IAAM,EAAQ,aAAe,EAAK,OAAO,IACnF,EAAQ,MAAM,KAAO,GAAG,EAAK,KAAK,IAQlC,IAAM,EAAgB,EAAS,QAC/B,GAAI,EAAe,CACjB,IAAM,EAAe,IAAkB,EAAS,EAAI,EAAc,WAC5D,EAAY,EAAK,KAAO,EAExB,EADa,EAAY,EAAc,aACT,EAAG,KAAO,EAAG,MAAQ,GACnD,EAAe,EAAG,KAAO,EAAc,EACzC,EAAgB,EAClB,EAAQ,MAAM,KAAO,GAAG,EAAK,KAAO,EAAc,IACzC,EAAe,IACxB,EAAQ,MAAM,KAAO,GAAG,EAAK,KAAO,EAAa,IAErD,CAEA,EAAQ,MAAM,WAAa,EAAsB,EAAM,CAAS,EAAI,SAAW,GAG/E,IAAM,EAAQ,EAAS,QACnB,GAAS,CAAC,EAAS,IAAI,CAAK,GAAG,EAAmB,CACxD,CAMA,IAAM,EAAiB,IAAI,mBAAqB,EAAU,CAAC,EACrD,EAAW,IAAI,IACrB,SAAS,GAAqB,CAI5B,CAAC,EAAQ,EAAS,OAAO,EAAE,QAAS,GAAO,CACrC,CAAC,GAAM,EAAS,IAAI,CAAE,IAC1B,EAAS,IAAI,CAAE,EACf,EAAe,QAAQ,CAAE,EAC3B,CAAC,CACH,CAEA,EAAU,EAEV,IAAM,EAA0C,CAAC,OAAQ,GAAG,CAAS,EACrE,EAAc,QAAS,GAAM,EAAE,iBAAiB,SAAU,EAAW,CAAE,QAAS,EAAK,CAAC,CAAC,EACvF,OAAO,iBAAiB,SAAU,CAAS,EAG3C,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAC3D,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAC3D,MAAiB,CACf,EAAc,QAAS,GAAM,EAAE,oBAAoB,SAAU,CAAS,CAAC,EACvE,OAAO,oBAAoB,SAAU,CAAS,EAC9C,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,EAAe,WAAW,EAI1B,EAAQ,MAAM,IAAM,GACpB,EAAQ,MAAM,KAAO,GACrB,EAAQ,MAAM,WAAa,GAC3B,EAAQ,UAAU,OAAO,CAAa,CACxC,CACF,CAIA,OAFA,EAAS,MAEI,CACP,IAAU,IAAA,IAAW,qBAAqB,CAAK,EACnD,IAAW,CACb,CACF,EAAG,CAAC,EAAQ,EAAY,EAAW,EAAU,EAAe,CAAS,CAAC,CACxE"}
|
|
1
|
+
{"version":3,"file":"useFloatingPosition.mjs","names":[],"sources":["../../../../package/common/floatingMenu/useFloatingPosition.ts"],"sourcesContent":["import { type RefObject } from 'react';\nimport { isHiddenByAnyAncestor } from 'components/common/utils/scrollAncestors';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\nimport { useAnchorTracking } from '../hooks/useAnchorTracking';\n\nexport interface UseFloatingPositionArgs {\n isOpen: boolean;\n /** The element the popup is anchored to. */\n triggerRef: RefObject<HTMLElement | null>;\n /** The `position: fixed` element that gets `top`/`left`/`visibility` written to it directly. */\n anchorRef: RefObject<HTMLElement | null>;\n /** The visible panel whose height decides whether there's room to render on the preferred side. */\n panelRef: RefObject<HTMLElement | null>;\n /** Class toggled on `anchorRef` when flipped to the non-preferred side. */\n flipClassName: string;\n /** Preferred side relative to the trigger (default `'bottom'`) — flips to the other side when there's no room. */\n placement?: 'top' | 'bottom';\n}\n\n/** Minimum gap kept between the panel and the viewport edge when horizontally clamped. */\nconst EDGE_MARGIN = 8;\n\n/**\n * Keeps a portaled, `position: fixed` popup anchored to a trigger element: flips above the\n * trigger when there's no room below, and hides (not closes) the popup when the trigger itself\n * scrolls behind a clipping ancestor — matching Floating UI's autoUpdate + hide middleware.\n * It also shifts the popup horizontally to stay within the viewport: `align`/RTL-style CSS\n * only positions the panel relative to the anchor's own box and has no awareness of where\n * that box actually sits on screen, so a trigger near a screen edge would otherwise let the\n * panel overflow it with no correction.\n *\n * `useAnchorTracking` supplies the lifecycle around this: mount wait, ancestor resolution, and\n * re-running on scroll, viewport resize and panel size changes.\n */\nexport function useFloatingPosition({ isOpen, triggerRef, anchorRef, panelRef, flipClassName, placement = 'bottom' }: UseFloatingPositionArgs) {\n useAnchorTracking({\n isOpen,\n triggerRef,\n elementRefs: [anchorRef],\n // Both, since they're the same element for a panel-as-anchor consumer (Dropdown) but not for\n // a zero-height wrapper whose content sits inside it (FloatingMenu).\n resizeRefs: [anchorRef, panelRef],\n deps: [anchorRef, panelRef, flipClassName, placement],\n\n update: (trigger, ancestors) => {\n const anchor = anchorRef.current;\n if (!anchor) return;\n\n const rect = trigger.getBoundingClientRect();\n const panelHeight = panelRef.current?.offsetHeight ?? 0;\n // The visible area, not the full layout viewport — shrinks on mobile when a keyboard is\n // open, so flip/clamp react to what's actually on screen instead of the keyboard hiding\n // a panel that \"fit\" against the unshrunk layout viewport.\n const vv = getVisualViewportRect();\n const spaceBelow = vv.top + vv.height - rect.bottom;\n const spaceAbove = rect.top - vv.top;\n const preferredSpace = placement === 'top' ? spaceAbove : spaceBelow;\n const alternateSpace = placement === 'top' ? spaceBelow : spaceAbove;\n const flip = preferredSpace < panelHeight && alternateSpace > preferredSpace;\n const renderAbove = placement === 'top' ? !flip : flip;\n anchor.classList.toggle(flipClassName, flip);\n // Shift up by the anchor's own height when rendering above, so this works whether\n // the anchor is a zero-height decorative wrapper (FloatingMenu, whose real panel\n // hangs off it via absolute-positioned CSS) or the visible panel itself\n // (Dropdown) — for the former the shift is a no-op (height 0).\n anchor.style.top = `${renderAbove ? rect.top - anchor.offsetHeight : rect.bottom}px`;\n anchor.style.left = `${rect.left}px`;\n\n // Horizontal clamp: shift the anchor so the panel stays within the viewport, since\n // `align` CSS only positions it relative to the anchor's own box. Use offsetWidth/\n // offsetLeft (untransformed layout geometry), not getBoundingClientRect — both this\n // anchor and Dropdown's animate in via a CSS `transform: scale(...)`, which\n // getBoundingClientRect reflects but the box-model offsets don't, so measuring via\n // the transformed rect would under- or over-correct while that entrance animation runs.\n const panelForClamp = panelRef.current;\n if (panelForClamp) {\n const relativeLeft = panelForClamp === anchor ? 0 : panelForClamp.offsetLeft;\n const panelLeft = rect.left + relativeLeft;\n const panelRight = panelLeft + panelForClamp.offsetWidth;\n const overflowRight = panelRight - (vv.left + vv.width - EDGE_MARGIN);\n const overflowLeft = vv.left + EDGE_MARGIN - panelLeft;\n if (overflowRight > 0) {\n anchor.style.left = `${rect.left - overflowRight}px`;\n } else if (overflowLeft > 0) {\n anchor.style.left = `${rect.left + overflowLeft}px`;\n }\n }\n\n anchor.style.visibility = isHiddenByAnyAncestor(rect, ancestors) ? 'hidden' : '';\n },\n\n teardown: () => {\n // Positioning is written as inline styles, which outrank any class the popup might switch\n // to while it stays mounted — a popup turning into a bottom sheet (`useSheet`) would keep\n // the coordinates computed for its anchored self. Hand the element back as we found it.\n const anchor = anchorRef.current;\n if (!anchor) return;\n anchor.style.top = '';\n anchor.style.left = '';\n anchor.style.visibility = '';\n anchor.classList.remove(flipClassName);\n },\n });\n}\n"],"mappings":"4MAoBA,IAAM,EAAc,EAcpB,SAAgB,EAAoB,CAAE,SAAQ,aAAY,YAAW,WAAU,gBAAe,YAAY,UAAqC,CAC7I,EAAkB,CAChB,SACA,aACA,YAAa,CAAC,CAAS,EAGvB,WAAY,CAAC,EAAW,CAAQ,EAChC,KAAM,CAAC,EAAW,EAAU,EAAe,CAAS,EAEpD,QAAS,EAAS,IAAc,CAC9B,IAAM,EAAS,EAAU,QACzB,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAO,EAAQ,sBAAsB,EACrC,EAAc,EAAS,SAAS,cAAgB,EAIhD,EAAK,EAAsB,EAC3B,EAAa,EAAG,IAAM,EAAG,OAAS,EAAK,OACvC,EAAa,EAAK,IAAM,EAAG,IAC3B,EAAiB,IAAc,MAAQ,EAAa,EAEpD,EAAO,EAAiB,IADP,IAAc,MAAQ,EAAa,GACI,EACxD,EAAc,IAAc,MAAQ,CAAC,EAAO,EAClD,EAAO,UAAU,OAAO,EAAe,CAAI,EAK3C,EAAO,MAAM,IAAM,GAAG,EAAc,EAAK,IAAM,EAAO,aAAe,EAAK,OAAO,IACjF,EAAO,MAAM,KAAO,GAAG,EAAK,KAAK,IAQjC,IAAM,EAAgB,EAAS,QAC/B,GAAI,EAAe,CACjB,IAAM,EAAe,IAAkB,EAAS,EAAI,EAAc,WAC5D,EAAY,EAAK,KAAO,EAExB,EADa,EAAY,EAAc,aACT,EAAG,KAAO,EAAG,MAAQ,GACnD,EAAe,EAAG,KAAO,EAAc,EACzC,EAAgB,EAClB,EAAO,MAAM,KAAO,GAAG,EAAK,KAAO,EAAc,IACxC,EAAe,IACxB,EAAO,MAAM,KAAO,GAAG,EAAK,KAAO,EAAa,IAEpD,CAEA,EAAO,MAAM,WAAa,EAAsB,EAAM,CAAS,EAAI,SAAW,EAChF,EAEA,aAAgB,CAId,IAAM,EAAS,EAAU,QACpB,IACL,EAAO,MAAM,IAAM,GACnB,EAAO,MAAM,KAAO,GACpB,EAAO,MAAM,WAAa,GAC1B,EAAO,UAAU,OAAO,CAAa,EACvC,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getScrollableAncestors as e}from"../utils/scrollAncestors.mjs";import{useLayoutEffect as t,useRef as n}from"react";function r({isOpen:r,triggerRef:i,elementRefs:a,update:o,resizeRefs:s,teardown:c,deps:l=[]}){let u=n({elementRefs:a,update:o,resizeRefs:s,teardown:c});u.current={elementRefs:a,update:o,resizeRefs:s,teardown:c},t(()=>{if(!r)return;let t,n;function a(){let r=i.current,o=u.current.elementRefs;if(!r||o.some(e=>!e.current)){t=requestAnimationFrame(a);return}let s=e(r),c=new ResizeObserver(()=>d()),l=new Set;function d(){u.current.update(r,s),u.current.resizeRefs?.forEach(({current:e})=>{!e||l.has(e)||(l.add(e),c.observe(e))})}d();let f=[window,...s];f.forEach(e=>e.addEventListener(`scroll`,d,{passive:!0})),window.addEventListener(`resize`,d),window.visualViewport?.addEventListener(`resize`,d),window.visualViewport?.addEventListener(`scroll`,d),n=()=>{f.forEach(e=>e.removeEventListener(`scroll`,d)),window.removeEventListener(`resize`,d),window.visualViewport?.removeEventListener(`resize`,d),window.visualViewport?.removeEventListener(`scroll`,d),c.disconnect(),u.current.teardown?.()}}return a(),()=>{t!==void 0&&cancelAnimationFrame(t),n?.()}},[r,i,...l])}export{r as useAnchorTracking};
|
|
2
|
+
//# sourceMappingURL=useAnchorTracking.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAnchorTracking.mjs","names":[],"sources":["../../../../package/common/hooks/useAnchorTracking.ts"],"sourcesContent":["import { useLayoutEffect, useRef, type RefObject } from 'react';\nimport { getScrollableAncestors } from 'components/common/utils/scrollAncestors';\n\nexport interface UseAnchorTrackingArgs {\n isOpen: boolean;\n /** The element the popup is anchored to. Its scrollable ancestors are what get tracked. */\n triggerRef: RefObject<HTMLElement | null>;\n /**\n * Every element the positioning maths needs. Tracking starts only once all of them, plus the\n * trigger, are mounted. Read fresh on each setup, so an inline array literal is fine.\n */\n elementRefs: RefObject<HTMLElement | null>[];\n /**\n * Positions the popup. Called once on setup and then on every scroll, resize and tracked size\n * change. Reads the caller's own refs for everything beyond the two arguments.\n */\n update: (trigger: HTMLElement, ancestors: HTMLElement[]) => void;\n /**\n * Elements whose own size changes must re-run `update`: a popup whose content changes size\n * while open (a filtered list shrinking, an async list loading in) otherwise keeps coordinates\n * computed for its old height and ends up detached from the trigger. Re-read on every update,\n * since a panel can mount a commit later than the element it hangs off.\n */\n resizeRefs?: RefObject<HTMLElement | null>[];\n /** Hands the popup's elements back as they were found. Runs when tracking stops. */\n teardown?: () => void;\n /** Values that must restart tracking, and so re-run `update`, when they change. */\n deps?: readonly unknown[];\n}\n\n/**\n * The lifecycle half of anchoring a portaled, `position: fixed` popup to a trigger: waits for the\n * elements to mount, resolves the trigger's scrolling ancestors once, and re-runs the caller's\n * positioning on every scroll, viewport resize and tracked size change until it closes.\n *\n * Callers supply only the geometry (`update`) and the inline styles to clear again (`teardown`);\n * `useFloatingPosition` and `usePopoverPosition` are both built on this.\n *\n * Position is deliberately written straight to the DOM by the caller rather than through React\n * state: going through setState -> re-render -> commit adds a round-trip that lags behind the\n * browser's own scroll painting by at least a frame.\n */\nexport function useAnchorTracking({\n isOpen, triggerRef, elementRefs, update, resizeRefs, teardown, deps = [],\n}: UseAnchorTrackingArgs) {\n // Latest-value refs, so the effect restarts only for `deps` the caller names, never merely\n // because these were passed as fresh array/function literals this render.\n const latest = useRef({ elementRefs, update, resizeRefs, teardown });\n latest.current = { elementRefs, update, resizeRefs, teardown };\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let rafId: number | undefined;\n let stop: (() => void) | undefined;\n\n // The elements may not be mounted yet on the very first invocation of this effect: a portal\n // target (e.g. BodyEnd's `#bodyEnd` div) can still be missing on the initial render, in which\n // case the portaled content — and its ref — commits one render later. Since refs never change\n // identity, that later commit doesn't re-trigger this effect on its own, so retry each frame.\n function trySetup() {\n const trigger = triggerRef.current;\n const elements = latest.current.elementRefs;\n if (!trigger || elements.some((ref) => !ref.current)) {\n rafId = requestAnimationFrame(trySetup);\n return;\n }\n const ancestors = getScrollableAncestors(trigger);\n\n const resizeObserver = new ResizeObserver(() => runUpdate());\n const observed = new Set<Element>();\n\n function runUpdate() {\n latest.current.update(trigger!, ancestors);\n // After the update, so an element that only mounts during it is picked up too. Once\n // observed this is a handful of Set lookups, so it costs nothing on the scroll path.\n latest.current.resizeRefs?.forEach(({ current }) => {\n if (!current || observed.has(current)) return;\n observed.add(current);\n resizeObserver.observe(current);\n });\n }\n\n runUpdate();\n\n const scrollTargets: (Window | HTMLElement)[] = [window, ...ancestors];\n scrollTargets.forEach((t) => t.addEventListener('scroll', runUpdate, { passive: true }));\n window.addEventListener('resize', runUpdate);\n // `window.resize` doesn't reliably fire when a mobile keyboard opens/closes — visualViewport\n // does, and is what the callers' flip/clamp maths is measured against.\n window.visualViewport?.addEventListener('resize', runUpdate);\n window.visualViewport?.addEventListener('scroll', runUpdate);\n\n stop = () => {\n scrollTargets.forEach((t) => t.removeEventListener('scroll', runUpdate));\n window.removeEventListener('resize', runUpdate);\n window.visualViewport?.removeEventListener('resize', runUpdate);\n window.visualViewport?.removeEventListener('scroll', runUpdate);\n resizeObserver.disconnect();\n latest.current.teardown?.();\n };\n }\n\n trySetup();\n\n return () => {\n if (rafId !== undefined) cancelAnimationFrame(rafId);\n stop?.();\n };\n // Spread rather than listed: `deps` is the caller's own set of geometry inputs, and changing\n // any of them has to restart tracking so the new values get applied immediately.\n }, [isOpen, triggerRef, ...deps]);\n}\n"],"mappings":"0HA0CA,SAAgB,EAAkB,CAChC,SAAQ,aAAY,cAAa,SAAQ,aAAY,WAAU,OAAO,CAAC,GAC/C,CAGxB,IAAM,EAAS,EAAO,CAAE,cAAa,SAAQ,aAAY,UAAS,CAAC,EACnE,EAAO,QAAU,CAAE,cAAa,SAAQ,aAAY,UAAS,EAE7D,MAAsB,CACpB,GAAI,CAAC,EAAQ,OAEb,IAAI,EACA,EAMJ,SAAS,GAAW,CAClB,IAAM,EAAU,EAAW,QACrB,EAAW,EAAO,QAAQ,YAChC,GAAI,CAAC,GAAW,EAAS,KAAM,GAAQ,CAAC,EAAI,OAAO,EAAG,CACpD,EAAQ,sBAAsB,CAAQ,EACtC,MACF,CACA,IAAM,EAAY,EAAuB,CAAO,EAE1C,EAAiB,IAAI,mBAAqB,EAAU,CAAC,EACrD,EAAW,IAAI,IAErB,SAAS,GAAY,CACnB,EAAO,QAAQ,OAAO,EAAU,CAAS,EAGzC,EAAO,QAAQ,YAAY,SAAS,CAAE,aAAc,CAC9C,CAAC,GAAW,EAAS,IAAI,CAAO,IACpC,EAAS,IAAI,CAAO,EACpB,EAAe,QAAQ,CAAO,EAChC,CAAC,CACH,CAEA,EAAU,EAEV,IAAM,EAA0C,CAAC,OAAQ,GAAG,CAAS,EACrE,EAAc,QAAS,GAAM,EAAE,iBAAiB,SAAU,EAAW,CAAE,QAAS,EAAK,CAAC,CAAC,EACvF,OAAO,iBAAiB,SAAU,CAAS,EAG3C,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAC3D,OAAO,gBAAgB,iBAAiB,SAAU,CAAS,EAE3D,MAAa,CACX,EAAc,QAAS,GAAM,EAAE,oBAAoB,SAAU,CAAS,CAAC,EACvE,OAAO,oBAAoB,SAAU,CAAS,EAC9C,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,OAAO,gBAAgB,oBAAoB,SAAU,CAAS,EAC9D,EAAe,WAAW,EAC1B,EAAO,QAAQ,WAAW,CAC5B,CACF,CAIA,OAFA,EAAS,MAEI,CACP,IAAU,IAAA,IAAW,qBAAqB,CAAK,EACnD,IAAO,CACT,CAGF,EAAG,CAAC,EAAQ,EAAY,GAAG,CAAI,CAAC,CAClC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useCallback as e,useEffect as t,useRef as n,useState as r}from"react";import{Fragment as i,jsx as a}from"react/jsx-runtime";var o=[`click`,`mousemove`,`keydown`,`scroll`,`touchstart`];function s({children:s=null,idleAfterSeconds:c=600,renderCondition:l=!0}){let[u,d]=r(!1),f=n(null),p=e(()=>{f.current!==null&&clearTimeout(f.current),
|
|
1
|
+
import{useCallback as e,useEffect as t,useRef as n,useState as r}from"react";import{Fragment as i,jsx as a}from"react/jsx-runtime";var o=[`click`,`mousemove`,`keydown`,`scroll`,`touchstart`];function s({children:s=null,idleAfterSeconds:c=600,renderCondition:l=!0}){let[u,d]=r(!1),f=n(null),p=e(()=>{f.current!==null&&clearTimeout(f.current),f.current=setTimeout(()=>{d(!0)},c*1e3)},[c]),m=e(()=>{d(!1),p()},[p]);return t(()=>{p()},[p]),t(()=>{for(let e of o)document.addEventListener(e,m);return()=>{for(let e of o)document.removeEventListener(e,m);f.current!==null&&clearTimeout(f.current)}},[m]),a(i,{children:l&&u&&s})}export{s as default};
|
|
2
2
|
//# sourceMappingURL=idleManager.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idleManager.mjs","names":[],"sources":["../../../../package/common/idleManager/idleManager.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { IdleManagerProps } from './idleManager.types';\n\nconst ACTIVITY_EVENTS = ['click', 'mousemove', 'keydown', 'scroll', 'touchstart'] as const;\n\nfunction IdleManager({\n children = null,\n idleAfterSeconds = 600,\n renderCondition = true,\n}: IdleManagerProps) {\n const [isIdleContentShown, setIsIdleContentShown] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const
|
|
1
|
+
{"version":3,"file":"idleManager.mjs","names":[],"sources":["../../../../package/common/idleManager/idleManager.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { IdleManagerProps } from './idleManager.types';\n\nconst ACTIVITY_EVENTS = ['click', 'mousemove', 'keydown', 'scroll', 'touchstart'] as const;\n\nfunction IdleManager({\n children = null,\n idleAfterSeconds = 600,\n renderCondition = true,\n}: IdleManagerProps) {\n const [isIdleContentShown, setIsIdleContentShown] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const scheduleIdleTimeout = useCallback(() => {\n if (timeoutRef.current !== null) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n setIsIdleContentShown(true);\n }, idleAfterSeconds * 1000);\n }, [idleAfterSeconds]);\n\n // Only activity hides idle content again. Kept out of the effect below so that mounting, or a\n // change of `idleAfterSeconds`, restarts the countdown without dismissing what is on screen.\n const resetIdleTimeout = useCallback(() => {\n setIsIdleContentShown(false);\n scheduleIdleTimeout();\n }, [scheduleIdleTimeout]);\n\n useEffect(() => {\n scheduleIdleTimeout();\n }, [scheduleIdleTimeout]);\n\n // Register / unregister activity listeners\n useEffect(() => {\n for (const event of ACTIVITY_EVENTS) {\n document.addEventListener(event, resetIdleTimeout);\n }\n return () => {\n for (const event of ACTIVITY_EVENTS) {\n document.removeEventListener(event, resetIdleTimeout);\n }\n // Clear any pending timeout on unmount so it never fires after we're gone\n if (timeoutRef.current !== null) clearTimeout(timeoutRef.current);\n };\n }, [resetIdleTimeout]);\n\n return <>{renderCondition && isIdleContentShown && children}</>;\n}\n\nexport default IdleManager;\n"],"mappings":"mIAGA,IAAM,EAAkB,CAAC,QAAS,YAAa,UAAW,SAAU,YAAY,EAEhF,SAAS,EAAY,CACnB,WAAW,KACX,mBAAmB,IACnB,kBAAkB,IACC,CACnB,GAAM,CAAC,EAAoB,GAAyB,EAAS,EAAK,EAC5D,EAAa,EAA6C,IAAI,EAE9D,EAAsB,MAAkB,CACxC,EAAW,UAAY,MAAM,aAAa,EAAW,OAAO,EAChE,EAAW,QAAU,eAAiB,CACpC,EAAsB,EAAI,CAC5B,EAAG,EAAmB,GAAI,CAC5B,EAAG,CAAC,CAAgB,CAAC,EAIf,EAAmB,MAAkB,CACzC,EAAsB,EAAK,EAC3B,EAAoB,CACtB,EAAG,CAAC,CAAmB,CAAC,EAoBxB,OAlBA,MAAgB,CACd,EAAoB,CACtB,EAAG,CAAC,CAAmB,CAAC,EAGxB,MAAgB,CACd,IAAK,IAAM,KAAS,EAClB,SAAS,iBAAiB,EAAO,CAAgB,EAEnD,UAAa,CACX,IAAK,IAAM,KAAS,EAClB,SAAS,oBAAoB,EAAO,CAAgB,EAGlD,EAAW,UAAY,MAAM,aAAa,EAAW,OAAO,CAClE,CACF,EAAG,CAAC,CAAgB,CAAC,EAEd,EAAA,EAAA,CAAA,SAAG,GAAmB,GAAsB,CAAW,CAAA,CAChE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import t from"../tooltip/tooltip.mjs";import n from"../animatedIcon/animatedIcon.mjs";import r from"./input.module.mjs";import{InputIconPosition as i,InputType as a}from"./input.types.mjs";import ee,{useCallback as te,useEffect as o,useId as ne,useRef as
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import t from"../tooltip/tooltip.mjs";import n from"../animatedIcon/animatedIcon.mjs";import r from"./input.module.mjs";import{InputIconPosition as i,InputType as a}from"./input.types.mjs";import ee,{useCallback as te,useEffect as o,useId as ne,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as u}from"@fortawesome/react-fontawesome";import{faEnvelope as re,faEye as ie,faEyeSlash as ae,faPen as oe,faPhone as se,faSearch as ce,faXmark as le}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";function p(e){if(e)return{role:`button`,tabIndex:0,onKeyDown:t=>{t.key!==`Enter`&&t.key!==` `||(t.preventDefault(),e(t))}}}function m(m){let{name:ue=``,id:de,label:h=``,errorMessage:fe=``,isValid:pe=!0,className:me=``,style:he,value:ge=``,icon:_e=null,iconPosition:ve,onChange:g=()=>{},onFocus:_=null,onBlur:v=null,onMouseEnter:y=null,onMouseLeave:ye=null,isUppercase:be=!1,isRequired:xe=!1,tabIndex:Se=0,type:b=void 0,isRow:Ce=!1,onIconClick:x=null,parse:we=null,formValidator:S=null,autoFocus:Te=!1,suffix:Ee=null,upperRightLabel:De=null,labelInBorder:Oe=!1,showCancel:ke=!1,onClearClicked:Ae,min:je=null,max:Me=null,placeholder:C=``,useMatLabelStyle:w=!0,alwaysFloatLabel:Ne=!1,autoComplete:Pe=null,onClick:Fe=()=>null,readOnly:T=!1,customInput:E,alwaysShowCancel:Ie=!1,multiline:D=!1,rows:Le,autoResize:O=!1,classNames:k,styles:A,disabled:Re=!1,...j}=e(`Input`,m),[M,ze]=c(!1),[Be,N]=c(!1),[P,F]=c(!1),[,Ve]=c(0),I=s(null),L=s(S),He=ne(),R=de||ue||He,z=S?S.value:ge,B=s(!1),V=te(()=>{let e=I.current;if(e){if(!O){B.current&&=(e.style.height=``,!1);return}e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`,B.current=!0}},[O]);o(()=>{V()},[V,z]),o(()=>{Te&&I.current&&setTimeout(()=>{I.current?.focus()},1)},[Te]),o(()=>{L.current=S;let e=L.current;if(!e)return;e.label=h??``,e.validate();let t=()=>Ve(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[S,h]);let Ue=()=>S?S.touched&&S.hasError():!pe||!!fe,We=()=>!!(xe||S&&S.validators.find(e=>e.validatorId===`required`)),Ge=e=>{N(!0),y&&y(e)},Ke=e=>{N(!1),ye&&ye(e)},qe=e=>{ze(!0),_&&_(e)},Je=e=>{L.current&&(L.current.touched=!0),ze(!1),v&&v(e)},Ye=e=>{let t,n;typeof e==`string`?n=e:(t=e,n=e.target.value),we&&(n=we(n)),S&&S.set(n),g&&g(n,t),V()},Xe=()=>{if(b!==a.Email)return b===a.Phone?`tel`:b===a.Search?`search`:b===`password`&&P?`text`:b},Ze=()=>{if(b===a.Email)return`email`;if(b===a.Phone)return`tel`;if(b===a.Search)return`search`},Qe=b&&b!==a.Date&&b!==a.Iban&&b!==a.HasNoIcon,$e=b===a.Search||b===a.Password,H=!!_e,et=H&&!$e,tt=b===a.Email||b===a.Phone||b===a.Search,U=(ve??(!w&&tt?i.Left:i.Right))===i.Left,W={className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!x,[r.inputFieldIconLeft]:U}),onClick:x??void 0,...p(x)},G=_e,K=x??G?.props.onClick,nt=et&&G?ee.cloneElement(G,{className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!K,[r.inputFieldIconLeft]:U},G.props.className),onClick:K,...p(K)}):null,rt=(Qe||H)&&U,it={className:l(r.inputFieldIcon,r.inputFieldIconPassword,{[r.inputFieldIconLeft]:U}),onClick:()=>F(!P),...p(()=>F(!P)),"aria-label":P?`Hide password`:`Show password`},q=Ue(),J=h;J&&We()&&(J=`${J} *`);let at=We()?d(`span`,{className:r.requiredMark,children:`*`}):null,Y=M&&!T,X=w&&(Ne||Y||z||b===a.Date),Z=b===a.Search&&!!z,Q=E?.props;function $(){S&&S.set(``),g?.(``),Ae?.()}return f(`div`,{className:l(r.input,{[r.inputIconLeft]:rt},me),style:he,children:[f(`label`,{htmlFor:R,className:l(r.inputLabel,k?.label,{[r.inputLabelColumn]:!Ce,[r.inputLabelInBorder]:Oe}),style:A?.label,children:[h&&!w?f(`span`,{className:r.inputLabelText,children:[h,at]}):null,De&&d(`div`,{className:l(r.inputUpperRightLabel,k?.upperRightLabel),style:A?.upperRightLabel,children:De}),f(`div`,{className:l(r.inputContainer,{[r.inputContainerMultiline]:D},k?.container),style:A?.container,onMouseEnter:Ge,onMouseLeave:Ke,children:[nt,ee.cloneElement(E||d(D?`textarea`:`input`,{}),{...j,className:l(r.inputField,{[r.inputFieldUppercase]:be,[r.inputFieldDate]:b===a.Date,[r.inputFieldDefault]:b!==a.Date,[r.inputFieldWithIcon]:Qe||H,[r.inputFieldWithIconLeft]:rt,[r.inputFieldMultiline]:D,[r.inputFieldMultilineAutoResize]:D&&O,[r.inputFieldReadOnly]:T&&!Re},k?.input,Q?.className),style:Q?.style||A?.input?{...Q?.style,...A?.input}:void 0,name:ue,id:R,value:z,tabIndex:Se,ref:E?void 0:I,onChange:Ye,onFocus:qe,onBlur:Je,placeholder:w?``:C,autoComplete:Pe,onClick:Fe,readOnly:T,disabled:Re,...!D&&{type:Xe(),inputMode:j.inputMode??Ze(),min:je,max:Me},...D&&Le!==void 0&&{rows:Le}}),Ee&&d(`div`,{className:l(r.inputFieldSuffix,k?.suffix),style:A?.suffix,children:Ee})]}),d(`fieldset`,{className:l(r.inputFieldset,k?.fieldset,{[r.inputFieldsetNoNotch]:!h,[r.inputContainerBorderFocused]:Y,[r.inputContainerBorderSearchFocused]:b===a.Search&&(Y||!!z),[r.inputContainerBorderInvalid]:q,[r.inputContainerBorderError]:q}),style:A?.fieldset,children:d(`legend`,{className:l(r.inputFieldsetLegend,{[r.inputFieldsetLegendActive]:X}),children:d(`span`,{children:J})})}),w&&h&&f(`div`,{className:l(r.inputMatLabel,{[r.inputMatLabelFloating]:X,[r.inputMatLabelMultiline]:D&&!X}),children:[h,at]}),w&&C&&d(`div`,{className:l(r.inputPlaceholder,Y&&!z&&r.inputPlaceholderShown,D&&r.inputPlaceholderMultiline),children:C}),d(t,{variant:`error`,message:S?S.getCurrentErrorMessage():fe||``,isVisible:q&&(M||Be)})]}),b===a.Phone&&!H&&d(u,{icon:se,...W}),b===a.Email&&!H&&d(u,{icon:re,...W}),b===a.Edit&&!H&&d(u,{icon:oe,...W}),b===a.Search&&d(n,{icon:Z?le:ce,className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!Z,[r.inputFieldIconLeft]:U}),onClick:Z?$:void 0,...p(Z?$:void 0),"aria-label":Z?`Clear search`:void 0}),b===a.Password&&d(u,{icon:P?ae:ie,...it}),(ke&&z&&(M||Be)||Ie)&&b!==a.Search&&d(u,{icon:le,className:r.inputFieldIcon,onClick:$,onMouseEnter:()=>N(!0),onMouseLeave:()=>N(!1),...p($),"aria-label":`Clear`})]})}export{m as default};
|
|
2
2
|
//# sourceMappingURL=input.mjs.map
|