@ahrowe/ui 0.31.0 → 0.33.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/actionButtons/actionButtons.mjs +1 -1
- package/dist/esm/common/actionButtons/actionButtons.mjs.map +1 -1
- package/dist/esm/common/alert/alert.mjs +1 -1
- package/dist/esm/common/alert/alert.mjs.map +1 -1
- package/dist/esm/common/alert/alert.types.mjs.map +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
- package/dist/esm/common/card/Card.mjs +1 -1
- package/dist/esm/common/card/Card.mjs.map +1 -1
- package/dist/esm/common/card/card.types.mjs.map +1 -1
- package/dist/esm/common/carousel/carousel.mjs +1 -1
- package/dist/esm/common/carousel/carousel.mjs.map +1 -1
- package/dist/esm/common/chip/chip.mjs +1 -1
- package/dist/esm/common/chip/chip.mjs.map +1 -1
- package/dist/esm/common/chip/chip.types.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/configProvider/useLabels.mjs +2 -0
- package/dist/esm/common/configProvider/useLabels.mjs.map +1 -0
- package/dist/esm/common/confirmModal/confirmModal.mjs +1 -1
- package/dist/esm/common/confirmModal/confirmModal.mjs.map +1 -1
- package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs +1 -1
- package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs.map +1 -1
- package/dist/esm/common/datePicker/datePicker.mjs +1 -1
- package/dist/esm/common/datePicker/datePicker.mjs.map +1 -1
- package/dist/esm/common/drawer/drawer.mjs +1 -1
- package/dist/esm/common/drawer/drawer.mjs.map +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs.map +1 -1
- package/dist/esm/common/emptyState/emptyState.mjs +1 -1
- package/dist/esm/common/emptyState/emptyState.mjs.map +1 -1
- package/dist/esm/common/fab/components/fabBase/fabBase.mjs +1 -1
- package/dist/esm/common/fab/components/fabBase/fabBase.mjs.map +1 -1
- package/dist/esm/common/fab/fab.mjs +1 -1
- package/dist/esm/common/fab/fab.mjs.map +1 -1
- package/dist/esm/common/infiniteBlock/infiniteBlock.mjs +1 -1
- package/dist/esm/common/infiniteBlock/infiniteBlock.mjs.map +1 -1
- package/dist/esm/common/input/input.mjs +1 -1
- package/dist/esm/common/input/input.mjs.map +1 -1
- package/dist/esm/common/input/input.types.mjs.map +1 -1
- package/dist/esm/common/klipyPicker/klipyPicker.mjs +1 -1
- package/dist/esm/common/klipyPicker/klipyPicker.mjs.map +1 -1
- package/dist/esm/common/modal/modal.mjs +1 -1
- package/dist/esm/common/modal/modal.mjs.map +1 -1
- package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
- package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
- package/dist/esm/common/numberInput/numberInput.mjs +1 -1
- package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
- package/dist/esm/common/otpInput/otpInput.mjs +1 -1
- package/dist/esm/common/otpInput/otpInput.mjs.map +1 -1
- package/dist/esm/common/otpInput/otpInput.types.mjs.map +1 -1
- package/dist/esm/common/pagination/pagination.mjs +1 -1
- package/dist/esm/common/pagination/pagination.mjs.map +1 -1
- package/dist/esm/common/progressBar/progressBar.mjs +1 -1
- package/dist/esm/common/progressBar/progressBar.mjs.map +1 -1
- package/dist/esm/common/roomDrawer/roomDrawer.mjs +1 -1
- package/dist/esm/common/roomDrawer/roomDrawer.mjs.map +1 -1
- package/dist/esm/common/searchInput/searchInput.mjs +1 -1
- package/dist/esm/common/searchInput/searchInput.mjs.map +1 -1
- package/dist/esm/common/splitButton/splitButton.mjs +1 -1
- package/dist/esm/common/splitButton/splitButton.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/toast/toast.mjs +1 -1
- package/dist/esm/common/toast/toast.mjs.map +1 -1
- package/dist/esm/common/toast/toastProvider.mjs +1 -1
- package/dist/esm/common/toast/toastProvider.mjs.map +1 -1
- package/dist/esm/common/tree/tree.mjs +1 -1
- package/dist/esm/common/tree/tree.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualListHeader.mjs +1 -1
- package/dist/esm/common/virtualList/virtualListHeader.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/types/common/actionButtons/actionButtons.types.d.ts +9 -2
- package/dist/types/common/alert/alert.types.d.ts +7 -0
- package/dist/types/common/breadcrumb/breadcrumb.types.d.ts +7 -0
- package/dist/types/common/card/card.types.d.ts +6 -1
- package/dist/types/common/carousel/carousel.types.d.ts +13 -0
- package/dist/types/common/chip/chip.types.d.ts +13 -2
- package/dist/types/common/colorPicker/colorPicker.d.ts +1 -1
- package/dist/types/common/colorPicker/colorPicker.types.d.ts +13 -1
- package/dist/types/common/configProvider/configProvider.types.d.ts +26 -1
- package/dist/types/common/configProvider/index.d.ts +1 -0
- package/dist/types/common/configProvider/useLabels.d.ts +22 -0
- package/dist/types/common/confirmModal/confirmModal.types.d.ts +13 -4
- package/dist/types/common/datePicker/components/datePickerMonth/datePickerMonth.d.ts +2 -14
- package/dist/types/common/datePicker/components/datePickerMonth/datePickerMonth.types.d.ts +23 -0
- package/dist/types/common/datePicker/datePicker.d.ts +1 -22
- package/dist/types/common/datePicker/datePicker.types.d.ts +60 -0
- package/dist/types/common/datePicker/index.d.ts +1 -0
- package/dist/types/common/drawer/drawer.d.ts +1 -1
- package/dist/types/common/drawer/drawer.types.d.ts +7 -1
- package/dist/types/common/dropdown/dropdown.types.d.ts +6 -6
- package/dist/types/common/emptyState/emptyState.d.ts +1 -1
- package/dist/types/common/emptyState/emptyState.types.d.ts +13 -1
- package/dist/types/common/fab/components/fabBase/fabBase.d.ts +1 -1
- package/dist/types/common/fab/components/fabBase/fabBase.types.d.ts +3 -1
- package/dist/types/common/fab/fab.d.ts +1 -1
- package/dist/types/common/fab/fab.types.d.ts +6 -1
- package/dist/types/common/infiniteBlock/infiniteBlock.d.ts +1 -1
- package/dist/types/common/infiniteBlock/infiniteBlock.types.d.ts +2 -0
- package/dist/types/common/input/input.types.d.ts +13 -0
- package/dist/types/common/klipyPicker/klipyPicker.d.ts +1 -1
- package/dist/types/common/klipyPicker/klipyPicker.types.d.ts +7 -0
- package/dist/types/common/modal/modal.types.d.ts +7 -0
- package/dist/types/common/multiSelect/multiSelect.types.d.ts +13 -6
- package/dist/types/common/numberInput/numberInput.types.d.ts +15 -2
- package/dist/types/common/otpInput/otpInput.types.d.ts +9 -0
- package/dist/types/common/pagination/pagination.types.d.ts +15 -0
- package/dist/types/common/progressBar/progressBar.d.ts +1 -1
- package/dist/types/common/progressBar/progressBar.types.d.ts +6 -1
- package/dist/types/common/roomDrawer/roomDrawer.types.d.ts +6 -0
- package/dist/types/common/searchInput/searchInput.d.ts +1 -1
- package/dist/types/common/searchInput/searchInput.types.d.ts +13 -1
- package/dist/types/common/splitButton/splitButton.types.d.ts +7 -0
- package/dist/types/common/timer/timer.types.d.ts +7 -0
- package/dist/types/common/toast/toast.d.ts +4 -2
- package/dist/types/common/toast/toast.types.d.ts +11 -1
- package/dist/types/common/toast/toastProvider.d.ts +1 -1
- package/dist/types/common/tree/tree.types.d.ts +7 -2
- package/dist/types/common/virtualList/virtualList.types.d.ts +15 -6
- package/dist/types/common/virtualList/virtualListHeader.d.ts +3 -3
- package/docs/ActionButtons.md +7 -6
- package/docs/Alert.md +2 -0
- package/docs/Breadcrumb.md +2 -0
- package/docs/Card.md +2 -0
- package/docs/Carousel.md +2 -0
- package/docs/Chip.md +5 -1
- package/docs/ColorPicker.md +4 -0
- package/docs/ConfigProvider.md +23 -0
- package/docs/ConfirmModal.md +7 -5
- package/docs/DatePicker.md +6 -1
- package/docs/Drawer.md +2 -0
- package/docs/Dropdown.md +2 -0
- package/docs/EmptyState.md +4 -0
- package/docs/Fab.md +2 -0
- package/docs/FormValidatorGroup.md +1 -1
- package/docs/Input.md +2 -0
- package/docs/KlipyPicker.md +2 -0
- package/docs/Modal.md +2 -0
- package/docs/MultiSelect.md +7 -5
- package/docs/NumberInput.md +8 -2
- package/docs/OtpInput.md +2 -0
- package/docs/Pagination.md +2 -0
- package/docs/ProgressBar.md +2 -0
- package/docs/RoomDrawer.md +2 -0
- package/docs/SearchInput.md +4 -0
- package/docs/SplitButton.md +2 -0
- package/docs/Timer.md +2 -0
- package/docs/Toast.md +2 -0
- package/docs/Tree.md +3 -1
- package/docs/VirtualList.md +3 -2
- package/docs/Wizard.md +1 -2
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import t from"../tooltip/tooltip.mjs";import
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../tooltip/tooltip.mjs";import ee from"../animatedIcon/animatedIcon.mjs";import r from"./input.module.mjs";import{InputIconPosition as i,InputType as a}from"./input.types.mjs";import te,{useCallback as ne,useEffect as o,useId as re,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as u}from"@fortawesome/react-fontawesome";import{faEnvelope as ie,faEye as ae,faEyeSlash as oe,faPen as se,faPhone as ce,faSearch as le,faXmark as ue}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";var de={clear:`Clear`,showPassword:`Show password`,hidePassword:`Hide password`,clearSearch:`Clear search`};function p(e){if(e)return{role:`button`,tabIndex:0,onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),e(t))}}}function fe(fe){let{name:pe=``,id:me,label:m=``,errorMessage:he=``,isValid:ge=!0,className:_e=``,style:ve,value:ye=``,icon:h=null,iconPosition:be,onChange:g=()=>{},onFocus:_=null,onBlur:xe=null,onMouseEnter:Se=null,onMouseLeave:Ce=null,isUppercase:we=!1,isRequired:Te=!1,tabIndex:Ee=0,type:v=void 0,isRow:De=!1,onIconClick:y=null,parse:Oe=null,formValidator:b=null,autoFocus:x=!1,suffix:S=null,upperRightLabel:C=null,labelInBorder:ke=!1,showCancel:Ae=!1,onClearClicked:je,min:Me=null,max:Ne=null,placeholder:w=``,useMatLabelStyle:T=!0,alwaysFloatLabel:Pe=!1,autoComplete:Fe=null,onClick:Ie=()=>null,readOnly:E=!1,customInput:D,alwaysShowCancel:Le=!1,multiline:O=!1,rows:Re,autoResize:k=!1,classNames:A,styles:j,disabled:ze=!1,labels:Be,...M}=e(`Input`,fe),N=t(`Input`,Be,de),[P,Ve]=c(!1),[F,He]=c(!1),[Ue,I]=c(!1),[L,We]=c(!1),[,Ge]=c(0),R=s(null),z=s(b),Ke=re(),qe=me||pe||Ke,B=b?b.value:ye,Je=s(!1),V=ne(()=>{let e=R.current;if(e){if(!k){Je.current&&=(e.style.height=``,!1);return}e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`,Je.current=!0}},[k]);o(()=>{V()},[V,B]),o(()=>{x&&R.current&&setTimeout(()=>{R.current?.focus()},1)},[x]),o(()=>{z.current=b;let e=z.current;if(!e)return;e.label=m??``,e.validate();let t=()=>Ge(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[b,m]);let Ye=()=>b?b.touched&&b.hasError():!ge||!!he,Xe=()=>!!(Te||b&&b.validators.find(e=>e.validatorId===`required`)),Ze=e=>{I(!0),Se&&Se(e)},Qe=e=>{I(!1),Ce&&Ce(e)},$e=e=>{Ve(!0),_&&_(e)},et=e=>{z.current&&(z.current.touched=!0),Ve(!1),xe&&xe(e)},tt=e=>{let t,n;typeof e==`string`?n=e:(t=e,n=e.target.value),Oe&&(n=Oe(n)),b&&b.set(n),g&&g(n,t),V()},nt=()=>{if(v!==a.Email)return v===a.Phone?`tel`:v===a.Search?`search`:v===`password`&&L?`text`:v},rt=()=>{if(v===a.Email)return`email`;if(v===a.Phone)return`tel`;if(v===a.Search)return`search`},it=v&&v!==a.Date&&v!==a.Iban&&v!==a.HasNoIcon,at=v===a.Search||v===a.Password,H=!!h,ot=H&&!at,st=v===a.Email||v===a.Phone||v===a.Search,U=(be??(!T&&st?i.Left:i.Right))===i.Left,W={className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!y,[r.inputFieldIconLeft]:U}),onClick:y??void 0,...p(y)},G=h,K=y??G?.props.onClick,ct=ot&&G?te.cloneElement(G,{className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!K,[r.inputFieldIconLeft]:U},G.props.className),onClick:K,...p(K)}):null,lt=(it||H)&&U,ut={className:l(r.inputFieldIcon,r.inputFieldIconPassword,{[r.inputFieldIconLeft]:U}),onClick:()=>We(!L),...p(()=>We(!L)),"aria-label":L?N.hidePassword:N.showPassword},q=Ye(),J=m;J&&Xe()&&(J=`${J} *`);let dt=Xe()?d(`span`,{className:r.requiredMark,children:`*`}):null,Y=P&&!E,X=T&&(Pe||Y||B||F||v===a.Date),ft=e=>{let{animationName:t}=e;if(t===r.inputAutofillStart){He(!0);let t=e.currentTarget.value;t&&t!==String(B??``)&&tt(t);return}if(t===r.inputAutofillEnd){He(!1);return}M.onAnimationStart?.(e)},Z=v===a.Search&&!!B,Q=D?.props;function $(){b&&b.set(``),g?.(``),je?.()}return f(`div`,{className:l(r.input,{[r.inputIconLeft]:lt},_e),style:ve,children:[f(`label`,{htmlFor:qe,className:l(r.inputLabel,A?.label,{[r.inputLabelColumn]:!De,[r.inputLabelInBorder]:ke}),style:j?.label,children:[m&&!T?f(`span`,{className:r.inputLabelText,children:[m,dt]}):null,C&&d(`div`,{className:l(r.inputUpperRightLabel,A?.upperRightLabel),style:j?.upperRightLabel,children:C}),f(`div`,{className:l(r.inputContainer,{[r.inputContainerMultiline]:O},A?.container),style:j?.container,onMouseEnter:Ze,onMouseLeave:Qe,children:[ct,te.cloneElement(D||d(O?`textarea`:`input`,{}),{...M,className:l(r.inputField,{[r.inputFieldUppercase]:we,[r.inputFieldDate]:v===a.Date,[r.inputFieldDefault]:v!==a.Date,[r.inputFieldWithIcon]:it||H,[r.inputFieldWithIconLeft]:lt,[r.inputFieldMultiline]:O,[r.inputFieldMultilineAutoResize]:O&&k,[r.inputFieldReadOnly]:E&&!ze},A?.input,Q?.className),style:Q?.style||j?.input?{...Q?.style,...j?.input}:void 0,name:pe,id:qe,value:B,tabIndex:Ee,ref:D?void 0:R,onChange:tt,onAnimationStart:ft,onFocus:$e,onBlur:et,placeholder:T?``:w,autoComplete:Fe,onClick:Ie,readOnly:E,disabled:ze,...!O&&{type:nt(),inputMode:M.inputMode??rt(),min:Me,max:Ne},...O&&Re!==void 0&&{rows:Re}}),S&&d(`div`,{className:l(r.inputFieldSuffix,A?.suffix),style:j?.suffix,children:S})]}),d(`fieldset`,{className:l(r.inputFieldset,A?.fieldset,{[r.inputFieldsetNoNotch]:!m,[r.inputContainerBorderFocused]:Y,[r.inputContainerBorderSearchFocused]:v===a.Search&&(Y||!!B),[r.inputContainerBorderInvalid]:q,[r.inputContainerBorderError]:q}),style:j?.fieldset,children:d(`legend`,{className:l(r.inputFieldsetLegend,{[r.inputFieldsetLegendActive]:X}),children:d(`span`,{children:J})})}),T&&m&&f(`div`,{className:l(r.inputMatLabel,{[r.inputMatLabelFloating]:X,[r.inputMatLabelMultiline]:O&&!X}),children:[m,dt]}),T&&w&&d(`div`,{className:l(r.inputPlaceholder,Y&&!B&&!F&&r.inputPlaceholderShown,O&&r.inputPlaceholderMultiline),children:w}),d(n,{variant:`error`,message:b?b.getCurrentErrorMessage():he||``,isVisible:q&&(P||Ue)})]}),v===a.Phone&&!H&&d(u,{icon:ce,...W}),v===a.Email&&!H&&d(u,{icon:ie,...W}),v===a.Edit&&!H&&d(u,{icon:se,...W}),v===a.Search&&d(ee,{icon:Z?ue:le,className:l(r.inputFieldIcon,{[r.inputFieldIconNotClickable]:!Z,[r.inputFieldIconLeft]:U}),onClick:Z?$:void 0,...p(Z?$:void 0),"aria-label":Z?N.clearSearch:void 0}),v===a.Password&&d(u,{icon:L?oe:ae,...ut}),(Ae&&B&&(P||Ue)||Le)&&v!==a.Search&&d(u,{icon:ue,className:r.inputFieldIcon,onClick:$,onMouseEnter:()=>I(!0),onMouseLeave:()=>I(!1),...p($),"aria-label":N.clear})]})}export{fe as default};
|
|
2
2
|
//# sourceMappingURL=input.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.mjs","names":[],"sources":["../../../../package/common/input/input.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useId } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n faPhone,\n faPen,\n faEnvelope,\n faSearch,\n faXmark,\n faEye as passwordHiddenIcon,\n faEyeSlash as passwordShownIcon,\n} from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\nimport AnimatedIcon from 'components/common/animatedIcon';\nimport { useComponentDefaults } from 'components/common/configProvider';\nimport styles from './input.module.pcss';\nimport type { InputProps } from './input.types';\nimport { InputType, InputIconPosition } from './input.types';\n\nfunction keyboardActivationProps(onActivate?: ((event: React.SyntheticEvent) => void) | null) {\n if (!onActivate) return undefined;\n return {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: (event: React.KeyboardEvent) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n onActivate(event);\n },\n };\n}\n\nfunction Input(props: InputProps) {\n const {\n name = '',\n id,\n label = '',\n errorMessage = '',\n isValid = true,\n className = '',\n style,\n value = '',\n icon = null,\n iconPosition,\n onChange = () => {},\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n isUppercase = false,\n isRequired = false,\n tabIndex = 0,\n type = undefined,\n isRow = false,\n onIconClick = null,\n parse = null,\n formValidator = null,\n autoFocus = false,\n suffix = null,\n upperRightLabel = null,\n labelInBorder = false,\n showCancel = false,\n onClearClicked,\n min = null,\n max = null,\n placeholder = '',\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n autoComplete = null,\n onClick = () => null,\n readOnly = false,\n customInput,\n alwaysShowCancel = false,\n multiline = false,\n rows,\n autoResize = false,\n classNames,\n styles: slotStyles,\n disabled = false,\n ...rest\n } = useComponentDefaults('Input', props);\n const [isFocused, setIsFocused] = useState(false);\n // Chrome fills a field on page load without firing anything React sees. The animation the\n // stylesheet binds to :-webkit-autofill is the only notification it gives.\n const [isAutofilled, setIsAutofilled] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [isPasswordShown, setIsPasswordShown] = useState(false);\n const [, forceUpdate] = useState(0);\n\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const fvRef = useRef(formValidator);\n\n const generatedId = useId();\n const inputId = id || name || generatedId;\n\n const currVal = formValidator ? formValidator.value : value;\n const didSetAutoHeightRef = useRef(false);\n\n const resizeTextarea = useCallback(() => {\n const el = inputRef.current;\n if (!el) return;\n if (!autoResize) {\n // Only undo a height this set, so an explicit one from `styles.input` survives.\n if (didSetAutoHeightRef.current) {\n el.style.height = '';\n didSetAutoHeightRef.current = false;\n }\n return;\n }\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n didSetAutoHeightRef.current = true;\n }, [autoResize]);\n\n // `currVal` is a dependency because the value can change without passing through\n // `onChangeHandler`: the clear button, a parent resetting it, or `formValidator.set` elsewhere.\n useEffect(() => {\n resizeTextarea();\n }, [resizeTextarea, currVal]);\n\n useEffect(() => {\n if (autoFocus && inputRef.current) {\n setTimeout(() => {\n inputRef.current?.focus();\n }, 1);\n }\n }, [autoFocus]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label ?? '';\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired ||\n (formValidator &&\n formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n );\n };\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n if (onMouseEnterProp) onMouseEnterProp(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n if (onMouseLeaveProp) onMouseLeaveProp(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n if (onFocusProp) onFocusProp(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n if (onBlurProp) onBlurProp(event);\n };\n\n const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void => {\n let parsedEvent: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined;\n let val: string;\n\n if (typeof event !== 'string') {\n parsedEvent = event;\n val = event.target.value;\n } else {\n val = event;\n }\n\n if (parse) val = parse(val);\n if (formValidator) formValidator.set(val);\n if (onChange) onChange(val, parsedEvent);\n resizeTextarea();\n };\n\n const getInputType = (): string | undefined => {\n if (type === InputType.Email) return undefined;\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n if (type === 'password' && isPasswordShown) return 'text';\n return type;\n };\n\n const getInputMode = (): React.HTMLAttributes<HTMLInputElement>['inputMode'] => {\n if (type === InputType.Email) return 'email';\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n return undefined;\n };\n\n const hasIcon = type && type !== InputType.Date && type !== InputType.Iban && type !== InputType.HasNoIcon;\n\n const isFunctionalIconType = type === InputType.Search || type === InputType.Password;\n const hasCustomIcon = !!icon;\n const customIconApplies = hasCustomIcon && !isFunctionalIconType;\n\n const defaultsLeftWithoutMatLabel = type === InputType.Email || type === InputType.Phone || type === InputType.Search;\n const resolvedIconPosition =\n iconPosition ??\n (!useMatLabelStyle && defaultsLeftWithoutMatLabel ? InputIconPosition.Left : InputIconPosition.Right);\n const iconIsLeft = resolvedIconPosition === InputIconPosition.Left;\n\n const iconProps = {\n className: cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !onIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n }),\n onClick: onIconClick ?? undefined,\n ...keyboardActivationProps(onIconClick),\n };\n\n const customIcon = icon as React.ReactElement<{ className?: string; onClick?: React.MouseEventHandler }> | null;\n const resolvedCustomIconClick = onIconClick ?? customIcon?.props.onClick;\n const renderedIcon =\n customIconApplies && customIcon\n ? React.cloneElement(customIcon, {\n className: cx(\n styles.inputFieldIcon,\n {\n [styles.inputFieldIconNotClickable]: !resolvedCustomIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n },\n customIcon.props.className,\n ),\n onClick: resolvedCustomIconClick,\n ...keyboardActivationProps(resolvedCustomIconClick as ((event: React.SyntheticEvent) => void) | undefined),\n })\n : null;\n\n const hasLeftIcon = (hasIcon || hasCustomIcon) && iconIsLeft;\n\n const passwordIconProps = {\n className: cx(styles.inputFieldIcon, styles.inputFieldIconPassword, { [styles.inputFieldIconLeft]: iconIsLeft }),\n onClick: () => setIsPasswordShown(!isPasswordShown),\n ...keyboardActivationProps(() => setIsPasswordShown(!isPasswordShown)),\n 'aria-label': isPasswordShown ? 'Hide password' : 'Show password',\n };\n\n const hasErr = hasError();\n\n let editedLabel = label;\n if (editedLabel && computedIsRequired()) editedLabel = `${editedLabel} *`;\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n\n const showFocus = isFocused && !readOnly;\n const shouldFloatLabel =\n useMatLabelStyle && (alwaysFloatLabel || showFocus || currVal || isAutofilled || type === InputType.Date);\n\n const handleAutofillAnimation = (event: React.AnimationEvent) => {\n const { animationName } = event;\n if (animationName === styles.inputAutofillStart) {\n setIsAutofilled(true);\n // The field is controlled, so the commit this triggers would write `value` back over\n // what the browser just put there and blank it. Replay it as an ordinary change first.\n const filled = (event.currentTarget as HTMLInputElement).value;\n if (filled && filled !== String(currVal ?? '')) onChangeHandler(filled);\n return;\n }\n if (animationName === styles.inputAutofillEnd) {\n setIsAutofilled(false);\n return;\n }\n // Not ours. `onAnimationStart` arrives through `rest`, and the consumer must not receive\n // the two above: the `:not()` rule fires one of them on every mount.\n rest.onAnimationStart?.(event as React.AnimationEvent<HTMLInputElement>);\n };\n\n const isSearchCleared = type === InputType.Search && !!currVal;\n\n const customInputProps = customInput?.props as { className?: string; style?: React.CSSProperties } | undefined;\n\n function handleClear() {\n if (formValidator) formValidator.set('');\n onChange?.('');\n onClearClicked?.();\n }\n\n return (\n <div className={cx(styles.input, { [styles.inputIconLeft]: hasLeftIcon }, className)} style={style}>\n <label\n htmlFor={inputId}\n className={cx(styles.inputLabel, classNames?.label, {\n [styles.inputLabelColumn]: !isRow,\n [styles.inputLabelInBorder]: labelInBorder,\n })}\n style={slotStyles?.label}\n >\n {label && !useMatLabelStyle ? (\n <span className={styles.inputLabelText}>\n {label}\n {requiredMark}\n </span>\n ) : null}\n {upperRightLabel && (\n <div\n className={cx(styles.inputUpperRightLabel, classNames?.upperRightLabel)}\n style={slotStyles?.upperRightLabel}\n >\n {upperRightLabel}\n </div>\n )}\n <div\n className={cx(styles.inputContainer, { [styles.inputContainerMultiline]: multiline }, classNames?.container)}\n style={slotStyles?.container}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n {renderedIcon}\n {React.cloneElement(customInput || (multiline ? <textarea /> : <input />), {\n ...rest,\n className: cx(\n styles.inputField,\n {\n [styles.inputFieldUppercase]: isUppercase,\n [styles.inputFieldDate]: type === InputType.Date,\n [styles.inputFieldDefault]: type !== InputType.Date,\n [styles.inputFieldWithIcon]: hasIcon || hasCustomIcon,\n [styles.inputFieldWithIconLeft]: hasLeftIcon,\n [styles.inputFieldMultiline]: multiline,\n [styles.inputFieldMultilineAutoResize]: multiline && autoResize,\n [styles.inputFieldReadOnly]: readOnly && !disabled,\n },\n classNames?.input,\n customInputProps?.className,\n ),\n style:\n customInputProps?.style || slotStyles?.input\n ? { ...customInputProps?.style, ...slotStyles?.input }\n : undefined,\n name,\n id: inputId,\n value: currVal,\n tabIndex,\n ref: customInput ? undefined : inputRef,\n onChange: onChangeHandler,\n onAnimationStart: handleAutofillAnimation,\n onFocus: onFocusHandler,\n onBlur: onBlurHandler,\n placeholder: !useMatLabelStyle ? placeholder : '',\n autoComplete,\n onClick,\n readOnly,\n disabled,\n ...(!multiline && { type: getInputType(), inputMode: rest.inputMode ?? getInputMode(), min, max }),\n ...(multiline && rows !== undefined && { rows }),\n })}\n {suffix && (\n <div className={cx(styles.inputFieldSuffix, classNames?.suffix)} style={slotStyles?.suffix}>\n {suffix}\n </div>\n )}\n </div>\n <fieldset\n className={cx(styles.inputFieldset, classNames?.fieldset, {\n [styles.inputFieldsetNoNotch]: !label,\n [styles.inputContainerBorderFocused]: showFocus,\n [styles.inputContainerBorderSearchFocused]: type === InputType.Search && (showFocus || !!currVal),\n [styles.inputContainerBorderInvalid]: hasErr,\n [styles.inputContainerBorderError]: hasErr,\n })}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(styles.inputFieldsetLegend, {\n [styles.inputFieldsetLegendActive]: shouldFloatLabel,\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(styles.inputMatLabel, {\n [styles.inputMatLabelFloating]: shouldFloatLabel,\n [styles.inputMatLabelMultiline]: multiline && !shouldFloatLabel,\n })}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.inputPlaceholder,\n showFocus && !currVal && !isAutofilled && styles.inputPlaceholderShown,\n multiline && styles.inputPlaceholderMultiline,\n )}\n >\n {placeholder}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </label>\n\n {type === InputType.Phone && !hasCustomIcon && <FontAwesomeIcon icon={faPhone} {...iconProps} />}\n {type === InputType.Email && !hasCustomIcon && <FontAwesomeIcon icon={faEnvelope} {...iconProps} />}\n {type === InputType.Edit && !hasCustomIcon && <FontAwesomeIcon icon={faPen} {...iconProps} />}\n {type === InputType.Search && (\n <AnimatedIcon\n icon={isSearchCleared ? faXmark : faSearch}\n className={cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !isSearchCleared,\n [styles.inputFieldIconLeft]: iconIsLeft,\n })}\n onClick={isSearchCleared ? handleClear : undefined}\n {...keyboardActivationProps(isSearchCleared ? handleClear : undefined)}\n aria-label={isSearchCleared ? 'Clear search' : undefined}\n />\n )}\n {type === InputType.Password && (\n <FontAwesomeIcon icon={isPasswordShown ? passwordShownIcon : passwordHiddenIcon} {...passwordIconProps} />\n )}\n\n {((showCancel && currVal && (isFocused || isHovered)) || alwaysShowCancel) && type !== InputType.Search && (\n <FontAwesomeIcon\n icon={faXmark}\n className={styles.inputFieldIcon}\n onClick={handleClear}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...keyboardActivationProps(handleClear)}\n aria-label='Clear'\n />\n )}\n </div>\n );\n}\n\nexport default Input;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input.mjs","names":[],"sources":["../../../../package/common/input/input.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useId } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n faPhone,\n faPen,\n faEnvelope,\n faSearch,\n faXmark,\n faEye as passwordHiddenIcon,\n faEyeSlash as passwordShownIcon,\n} from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\nimport AnimatedIcon from 'components/common/animatedIcon';\nimport { useComponentDefaults, useLabels } from 'components/common/configProvider';\nimport styles from './input.module.pcss';\nimport type { InputLabels, InputProps } from './input.types';\nimport { InputType, InputIconPosition } from './input.types';\n\nconst DEFAULT_LABELS: Required<InputLabels> = {\n clear: 'Clear',\n showPassword: 'Show password',\n hidePassword: 'Hide password',\n clearSearch: 'Clear search',\n};\n\nfunction keyboardActivationProps(onActivate?: ((event: React.SyntheticEvent) => void) | null) {\n if (!onActivate) return undefined;\n return {\n role: 'button' as const,\n tabIndex: 0,\n onKeyDown: (event: React.KeyboardEvent) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n onActivate(event);\n },\n };\n}\n\nfunction Input(props: InputProps) {\n const {\n name = '',\n id,\n label = '',\n errorMessage = '',\n isValid = true,\n className = '',\n style,\n value = '',\n icon = null,\n iconPosition,\n onChange = () => {},\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n isUppercase = false,\n isRequired = false,\n tabIndex = 0,\n type = undefined,\n isRow = false,\n onIconClick = null,\n parse = null,\n formValidator = null,\n autoFocus = false,\n suffix = null,\n upperRightLabel = null,\n labelInBorder = false,\n showCancel = false,\n onClearClicked,\n min = null,\n max = null,\n placeholder = '',\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n autoComplete = null,\n onClick = () => null,\n readOnly = false,\n customInput,\n alwaysShowCancel = false,\n multiline = false,\n rows,\n autoResize = false,\n classNames,\n styles: slotStyles,\n disabled = false,\n labels,\n ...rest\n } = useComponentDefaults('Input', props);\n const text = useLabels('Input', labels, DEFAULT_LABELS);\n const [isFocused, setIsFocused] = useState(false);\n // Chrome fills a field on page load without firing anything React sees. The animation the\n // stylesheet binds to :-webkit-autofill is the only notification it gives.\n const [isAutofilled, setIsAutofilled] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [isPasswordShown, setIsPasswordShown] = useState(false);\n const [, forceUpdate] = useState(0);\n\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const fvRef = useRef(formValidator);\n\n const generatedId = useId();\n const inputId = id || name || generatedId;\n\n const currVal = formValidator ? formValidator.value : value;\n const didSetAutoHeightRef = useRef(false);\n\n const resizeTextarea = useCallback(() => {\n const el = inputRef.current;\n if (!el) return;\n if (!autoResize) {\n // Only undo a height this set, so an explicit one from `styles.input` survives.\n if (didSetAutoHeightRef.current) {\n el.style.height = '';\n didSetAutoHeightRef.current = false;\n }\n return;\n }\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n didSetAutoHeightRef.current = true;\n }, [autoResize]);\n\n // `currVal` is a dependency because the value can change without passing through\n // `onChangeHandler`: the clear button, a parent resetting it, or `formValidator.set` elsewhere.\n useEffect(() => {\n resizeTextarea();\n }, [resizeTextarea, currVal]);\n\n useEffect(() => {\n if (autoFocus && inputRef.current) {\n setTimeout(() => {\n inputRef.current?.focus();\n }, 1);\n }\n }, [autoFocus]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label ?? '';\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired ||\n (formValidator &&\n formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n );\n };\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n if (onMouseEnterProp) onMouseEnterProp(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n if (onMouseLeaveProp) onMouseLeaveProp(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n if (onFocusProp) onFocusProp(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n if (onBlurProp) onBlurProp(event);\n };\n\n const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void => {\n let parsedEvent: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined;\n let val: string;\n\n if (typeof event !== 'string') {\n parsedEvent = event;\n val = event.target.value;\n } else {\n val = event;\n }\n\n if (parse) val = parse(val);\n if (formValidator) formValidator.set(val);\n if (onChange) onChange(val, parsedEvent);\n resizeTextarea();\n };\n\n const getInputType = (): string | undefined => {\n if (type === InputType.Email) return undefined;\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n if (type === 'password' && isPasswordShown) return 'text';\n return type;\n };\n\n const getInputMode = (): React.HTMLAttributes<HTMLInputElement>['inputMode'] => {\n if (type === InputType.Email) return 'email';\n if (type === InputType.Phone) return 'tel';\n if (type === InputType.Search) return 'search';\n return undefined;\n };\n\n const hasIcon = type && type !== InputType.Date && type !== InputType.Iban && type !== InputType.HasNoIcon;\n\n const isFunctionalIconType = type === InputType.Search || type === InputType.Password;\n const hasCustomIcon = !!icon;\n const customIconApplies = hasCustomIcon && !isFunctionalIconType;\n\n const defaultsLeftWithoutMatLabel = type === InputType.Email || type === InputType.Phone || type === InputType.Search;\n const resolvedIconPosition =\n iconPosition ??\n (!useMatLabelStyle && defaultsLeftWithoutMatLabel ? InputIconPosition.Left : InputIconPosition.Right);\n const iconIsLeft = resolvedIconPosition === InputIconPosition.Left;\n\n const iconProps = {\n className: cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !onIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n }),\n onClick: onIconClick ?? undefined,\n ...keyboardActivationProps(onIconClick),\n };\n\n const customIcon = icon as React.ReactElement<{ className?: string; onClick?: React.MouseEventHandler }> | null;\n const resolvedCustomIconClick = onIconClick ?? customIcon?.props.onClick;\n const renderedIcon =\n customIconApplies && customIcon\n ? React.cloneElement(customIcon, {\n className: cx(\n styles.inputFieldIcon,\n {\n [styles.inputFieldIconNotClickable]: !resolvedCustomIconClick,\n [styles.inputFieldIconLeft]: iconIsLeft,\n },\n customIcon.props.className,\n ),\n onClick: resolvedCustomIconClick,\n ...keyboardActivationProps(resolvedCustomIconClick as ((event: React.SyntheticEvent) => void) | undefined),\n })\n : null;\n\n const hasLeftIcon = (hasIcon || hasCustomIcon) && iconIsLeft;\n\n const passwordIconProps = {\n className: cx(styles.inputFieldIcon, styles.inputFieldIconPassword, { [styles.inputFieldIconLeft]: iconIsLeft }),\n onClick: () => setIsPasswordShown(!isPasswordShown),\n ...keyboardActivationProps(() => setIsPasswordShown(!isPasswordShown)),\n 'aria-label': isPasswordShown ? text.hidePassword : text.showPassword,\n };\n\n const hasErr = hasError();\n\n let editedLabel = label;\n if (editedLabel && computedIsRequired()) editedLabel = `${editedLabel} *`;\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n\n const showFocus = isFocused && !readOnly;\n const shouldFloatLabel =\n useMatLabelStyle && (alwaysFloatLabel || showFocus || currVal || isAutofilled || type === InputType.Date);\n\n const handleAutofillAnimation = (event: React.AnimationEvent) => {\n const { animationName } = event;\n if (animationName === styles.inputAutofillStart) {\n setIsAutofilled(true);\n // The field is controlled, so the commit this triggers would write `value` back over\n // what the browser just put there and blank it. Replay it as an ordinary change first.\n const filled = (event.currentTarget as HTMLInputElement).value;\n if (filled && filled !== String(currVal ?? '')) onChangeHandler(filled);\n return;\n }\n if (animationName === styles.inputAutofillEnd) {\n setIsAutofilled(false);\n return;\n }\n // Not ours. `onAnimationStart` arrives through `rest`, and the consumer must not receive\n // the two above: the `:not()` rule fires one of them on every mount.\n rest.onAnimationStart?.(event as React.AnimationEvent<HTMLInputElement>);\n };\n\n const isSearchCleared = type === InputType.Search && !!currVal;\n\n const customInputProps = customInput?.props as { className?: string; style?: React.CSSProperties } | undefined;\n\n function handleClear() {\n if (formValidator) formValidator.set('');\n onChange?.('');\n onClearClicked?.();\n }\n\n return (\n <div className={cx(styles.input, { [styles.inputIconLeft]: hasLeftIcon }, className)} style={style}>\n <label\n htmlFor={inputId}\n className={cx(styles.inputLabel, classNames?.label, {\n [styles.inputLabelColumn]: !isRow,\n [styles.inputLabelInBorder]: labelInBorder,\n })}\n style={slotStyles?.label}\n >\n {label && !useMatLabelStyle ? (\n <span className={styles.inputLabelText}>\n {label}\n {requiredMark}\n </span>\n ) : null}\n {upperRightLabel && (\n <div\n className={cx(styles.inputUpperRightLabel, classNames?.upperRightLabel)}\n style={slotStyles?.upperRightLabel}\n >\n {upperRightLabel}\n </div>\n )}\n <div\n className={cx(styles.inputContainer, { [styles.inputContainerMultiline]: multiline }, classNames?.container)}\n style={slotStyles?.container}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n {renderedIcon}\n {React.cloneElement(customInput || (multiline ? <textarea /> : <input />), {\n ...rest,\n className: cx(\n styles.inputField,\n {\n [styles.inputFieldUppercase]: isUppercase,\n [styles.inputFieldDate]: type === InputType.Date,\n [styles.inputFieldDefault]: type !== InputType.Date,\n [styles.inputFieldWithIcon]: hasIcon || hasCustomIcon,\n [styles.inputFieldWithIconLeft]: hasLeftIcon,\n [styles.inputFieldMultiline]: multiline,\n [styles.inputFieldMultilineAutoResize]: multiline && autoResize,\n [styles.inputFieldReadOnly]: readOnly && !disabled,\n },\n classNames?.input,\n customInputProps?.className,\n ),\n style:\n customInputProps?.style || slotStyles?.input\n ? { ...customInputProps?.style, ...slotStyles?.input }\n : undefined,\n name,\n id: inputId,\n value: currVal,\n tabIndex,\n ref: customInput ? undefined : inputRef,\n onChange: onChangeHandler,\n onAnimationStart: handleAutofillAnimation,\n onFocus: onFocusHandler,\n onBlur: onBlurHandler,\n placeholder: !useMatLabelStyle ? placeholder : '',\n autoComplete,\n onClick,\n readOnly,\n disabled,\n ...(!multiline && { type: getInputType(), inputMode: rest.inputMode ?? getInputMode(), min, max }),\n ...(multiline && rows !== undefined && { rows }),\n })}\n {suffix && (\n <div className={cx(styles.inputFieldSuffix, classNames?.suffix)} style={slotStyles?.suffix}>\n {suffix}\n </div>\n )}\n </div>\n <fieldset\n className={cx(styles.inputFieldset, classNames?.fieldset, {\n [styles.inputFieldsetNoNotch]: !label,\n [styles.inputContainerBorderFocused]: showFocus,\n [styles.inputContainerBorderSearchFocused]: type === InputType.Search && (showFocus || !!currVal),\n [styles.inputContainerBorderInvalid]: hasErr,\n [styles.inputContainerBorderError]: hasErr,\n })}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(styles.inputFieldsetLegend, {\n [styles.inputFieldsetLegendActive]: shouldFloatLabel,\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(styles.inputMatLabel, {\n [styles.inputMatLabelFloating]: shouldFloatLabel,\n [styles.inputMatLabelMultiline]: multiline && !shouldFloatLabel,\n })}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.inputPlaceholder,\n showFocus && !currVal && !isAutofilled && styles.inputPlaceholderShown,\n multiline && styles.inputPlaceholderMultiline,\n )}\n >\n {placeholder}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </label>\n\n {type === InputType.Phone && !hasCustomIcon && <FontAwesomeIcon icon={faPhone} {...iconProps} />}\n {type === InputType.Email && !hasCustomIcon && <FontAwesomeIcon icon={faEnvelope} {...iconProps} />}\n {type === InputType.Edit && !hasCustomIcon && <FontAwesomeIcon icon={faPen} {...iconProps} />}\n {type === InputType.Search && (\n <AnimatedIcon\n icon={isSearchCleared ? faXmark : faSearch}\n className={cx(styles.inputFieldIcon, {\n [styles.inputFieldIconNotClickable]: !isSearchCleared,\n [styles.inputFieldIconLeft]: iconIsLeft,\n })}\n onClick={isSearchCleared ? handleClear : undefined}\n {...keyboardActivationProps(isSearchCleared ? handleClear : undefined)}\n aria-label={isSearchCleared ? text.clearSearch : undefined}\n />\n )}\n {type === InputType.Password && (\n <FontAwesomeIcon icon={isPasswordShown ? passwordShownIcon : passwordHiddenIcon} {...passwordIconProps} />\n )}\n\n {((showCancel && currVal && (isFocused || isHovered)) || alwaysShowCancel) && type !== InputType.Search && (\n <FontAwesomeIcon\n icon={faXmark}\n className={styles.inputFieldIcon}\n onClick={handleClear}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n {...keyboardActivationProps(handleClear)}\n aria-label={text.clear}\n />\n )}\n </div>\n );\n}\n\nexport default Input;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.types.mjs","names":[],"sources":["../../../../package/common/input/input.types.ts"],"sourcesContent":["import React from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\n\nexport enum InputType {\n Edit = 'edit',\n Phone = 'phone',\n Email = 'email',\n Iban = 'iban',\n Date = 'date',\n HasNoIcon = 'hasNoIcon',\n Search = 'searchInput',\n Password = 'password',\n}\n\nexport enum InputIconPosition {\n Left = 'left',\n Right = 'right',\n}\n\nexport type InputSlots = 'label' | 'container' | 'input' | 'suffix' | 'upperRightLabel' | 'fieldset';\n\ntype OverriddenInputAttributes =\n | 'onChange'\n | 'onFocus'\n | 'onBlur'\n | 'onMouseEnter'\n | 'onMouseLeave'\n | 'onClick'\n | 'type'\n | 'value'\n | 'min'\n | 'max'\n | 'autoComplete';\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, OverriddenInputAttributes>, DataProps {\n onMouseEnter?: ((event: React.MouseEvent) => void) | null;\n onMouseLeave?: ((event: React.MouseEvent) => void) | null;\n onFocus?: ((event: React.FocusEvent) => void) | null;\n onBlur?: ((event: React.FocusEvent) => void) | null;\n onChange?:\n | ((value: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void)\n | null;\n errorMessage?: string;\n isValid?: boolean;\n isRequired?: boolean;\n label?: string;\n name?: string;\n className?: string;\n style?: React.CSSProperties;\n value?: string | number;\n icon?: React.ReactElement | null;\n iconPosition?: InputIconPosition;\n isUppercase?: boolean;\n isRow?: boolean;\n tabIndex?: number;\n type?: InputType;\n onIconClick?: (() => void) | null;\n parse?: ((value: string) => string) | null;\n suffix?: string | React.ReactNode;\n disabled?: boolean;\n upperRightLabel?: string | React.ReactNode;\n labelInBorder?: boolean;\n showCancel?: boolean;\n onClearClicked?: (() => void) | null;\n min?: string | number | null;\n max?: string | number | null;\n placeholder?: string;\n useMatLabelStyle?: boolean;\n alwaysFloatLabel?: boolean;\n autoComplete?: string | null;\n onClick?: (() => void) | null;\n readOnly?: boolean;\n customInput?: React.ReactElement | null;\n autoFocus?: boolean;\n alwaysShowCancel?: boolean;\n formValidator?: FormValidator | null;\n multiline?: boolean;\n rows?: number;\n autoResize?: boolean;\n classNames?: SlotClassNames<InputSlots>;\n styles?: SlotStyles<InputSlots>;\n}\n"],"mappings":"AAIA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,KAAA,OACA,EAAA,MAAA,QACA,EAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,KAAA,OACA,EAAA,UAAA,YACA,EAAA,OAAA,cACA,EAAA,SAAA,YACF,EAAA,CAAA,CAAA,EAEY,EAAL,SAAA,EAAA,OACL,GAAA,KAAA,OACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"input.types.mjs","names":[],"sources":["../../../../package/common/input/input.types.ts"],"sourcesContent":["import React from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\n\nexport enum InputType {\n Edit = 'edit',\n Phone = 'phone',\n Email = 'email',\n Iban = 'iban',\n Date = 'date',\n HasNoIcon = 'hasNoIcon',\n Search = 'searchInput',\n Password = 'password',\n}\n\nexport enum InputIconPosition {\n Left = 'left',\n Right = 'right',\n}\n\nexport type InputSlots = 'label' | 'container' | 'input' | 'suffix' | 'upperRightLabel' | 'fieldset';\n\ntype OverriddenInputAttributes =\n | 'onChange'\n | 'onFocus'\n | 'onBlur'\n | 'onMouseEnter'\n | 'onMouseLeave'\n | 'onClick'\n | 'type'\n | 'value'\n | 'min'\n | 'max'\n | 'autoComplete';\n\n/** Text Input renders of its own, for translating it. */\nexport interface InputLabels {\n /** Clear button on a search input, read out by screen readers. */\n clear?: string;\n /** Reveals a password, read out by screen readers. */\n showPassword?: string;\n /** Hides a password again. */\n hidePassword?: string;\n /** Clears a search input, read out by screen readers. */\n clearSearch?: string;\n}\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, OverriddenInputAttributes>, DataProps {\n onMouseEnter?: ((event: React.MouseEvent) => void) | null;\n onMouseLeave?: ((event: React.MouseEvent) => void) | null;\n onFocus?: ((event: React.FocusEvent) => void) | null;\n onBlur?: ((event: React.FocusEvent) => void) | null;\n onChange?:\n | ((value: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void)\n | null;\n errorMessage?: string;\n isValid?: boolean;\n isRequired?: boolean;\n label?: string;\n name?: string;\n className?: string;\n style?: React.CSSProperties;\n value?: string | number;\n icon?: React.ReactElement | null;\n iconPosition?: InputIconPosition;\n isUppercase?: boolean;\n isRow?: boolean;\n tabIndex?: number;\n type?: InputType;\n onIconClick?: (() => void) | null;\n parse?: ((value: string) => string) | null;\n suffix?: string | React.ReactNode;\n disabled?: boolean;\n upperRightLabel?: string | React.ReactNode;\n labelInBorder?: boolean;\n showCancel?: boolean;\n onClearClicked?: (() => void) | null;\n min?: string | number | null;\n max?: string | number | null;\n placeholder?: string;\n useMatLabelStyle?: boolean;\n alwaysFloatLabel?: boolean;\n autoComplete?: string | null;\n onClick?: (() => void) | null;\n readOnly?: boolean;\n customInput?: React.ReactElement | null;\n autoFocus?: boolean;\n alwaysShowCancel?: boolean;\n formValidator?: FormValidator | null;\n multiline?: boolean;\n rows?: number;\n autoResize?: boolean;\n classNames?: SlotClassNames<InputSlots>;\n styles?: SlotStyles<InputSlots>;\n /** Overrides the text Input renders of its own. Merged per key. */\n labels?: InputLabels;\n}\n"],"mappings":"AAIA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,KAAA,OACA,EAAA,MAAA,QACA,EAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,KAAA,OACA,EAAA,UAAA,YACA,EAAA,OAAA,cACA,EAAA,SAAA,YACF,EAAA,CAAA,CAAA,EAEY,EAAL,SAAA,EAAA,OACL,GAAA,KAAA,OACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"../actionButtons/actionButtons.module.mjs";import{ButtonStyleType as
|
|
1
|
+
import{useLabels as e}from"../configProvider/useLabels.mjs";import t from"../actionButtons/actionButtons.module.mjs";import{ButtonStyleType as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import{Align as i}from"../floatingMenu/floatingMenu.types.mjs";import a from"../floatingMenu/floatingMenu.mjs";import o from"../chip/chip.mjs";import s from"../chip/chipContainer.mjs";import c from"../modal/modal.mjs";import l from"../../services/klipy.mjs";import u from"../searchInput/searchInput.mjs";import d from"./components/gifPreview/gifPreview.mjs";import f from"./components/gifView/gifView.mjs";import p from"./klipyPicker.module.mjs";import{useMemo as m,useState as h}from"react";import g from"classnames";import{Fragment as _,jsx as v,jsxs as y}from"react/jsx-runtime";var b={search:`Pick gif`};function x({className:x,token:S=``,isModal:C=!1,onSelect:w=()=>void 0,selected:T=null,labels:E}){let D=e(`KlipyPicker`,E,b),O=m(()=>new l(S),[S]),[k,A]=h(``),[j,M]=h([]),[N,P]=h([]),[F,I]=h(null),[L,R]=h(!1);async function z(e){if(A(e),!e.trim()){M([]),P([]),I(null);return}let[{results:t=[],next:n=null},{results:r=[]}]=await Promise.all([O.search(e,{limit:18,contentfilter:`medium`}),O.searchSuggestions(e)]);M(t),I(n),P(r)}async function B(){let{results:e=[],next:t=null}=await O.search(k,{limit:36,pos:F??void 0,contentfilter:`medium`});I(t),M(t=>[...t,...e])}async function V(e){await w(e),R(!1),e&&O.registerShare(k,e.id)}function H(){return v(s,{inline:!0,className:p.klipyPickerSuggestionsContainer,children:N.map(e=>v(o,{className:p.klipyPickerSuggestionsItem,text:e,onClick:()=>z(e)},e))})}function U(){return v(d,{previewItems:j,onSelect:V,onLoadAdditional:B})}function W(){return v(u,{label:D.search,value:k,onDebounce:z,onChange:A})}return C?y(`div`,{className:g(p.klipyPicker,x),children:[y(`div`,{className:t.actionButtons,children:[v(r,{onClick:()=>R(!0),children:`Pick gif`}),T&&v(r,{styleType:n.Delete,onClick:()=>V(null),children:`Remove gif`})]}),T&&v(f,{className:p.klipyPickerSelected,src:T}),v(c,{isOpen:L,onClose:()=>R(!1),children:y(`div`,{className:p.klipyPickerModalContent,children:[W(),H(),U()]})})]}):y(`div`,{className:g(p.klipyPicker,x),children:[v(a,{align:i.Left,isOpen:j.length>0&&L,onOpenChange:R,dontCloseOnChildClick:!0,content:y(_,{children:[H(),U()]}),children:W()}),T&&v(r,{styleType:n.Delete,onClick:()=>V(null),children:`Remove gif`}),T&&v(f,{className:p.klipyPickerSelected,src:T})]})}export{x as default};
|
|
2
2
|
//# sourceMappingURL=klipyPicker.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"klipyPicker.mjs","names":[],"sources":["../../../../package/common/klipyPicker/klipyPicker.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport cx from 'classnames';\nimport type { ReactElement, ReactNode } from 'react';\nimport KlipyService from 'services/klipy';\nimport type { KlipyResult } from 'services/klipy';\nimport SearchInput from '../searchInput';\nimport GifPreview from './components/gifPreview';\nimport FloatingMenu from '../floatingMenu';\nimport Button, { ButtonStyleType } from '../button';\nimport Modal from '../modal';\nimport Chip, { ChipContainer } from '../chip';\nimport GifView from './components/gifView';\nimport type { KlipyPickerProps } from './klipyPicker.types';\nimport styles from './klipyPicker.module.pcss';\nimport actionButtonStyles from 'components/common/actionButtons/actionButtons.module.pcss';\nimport { Align } from '../floatingMenu/floatingMenu.types';\n\nexport default function KlipyPicker({\n className,\n token = '',\n isModal = false,\n onSelect = () => undefined,\n selected = null,\n}: KlipyPickerProps): ReactElement {\n const klipyService = useMemo(() => new KlipyService(token), [token]);\n\n const [searchText, setSearchText] = useState('');\n const [currentPreviews, setCurrentPreviews] = useState<KlipyResult[]>([]);\n const [currentSuggestions, setCurrentSuggestions] = useState<string[]>([]);\n const [lastNext, setLastNext] = useState<string | null>(null);\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n\n async function searchFor(query: string) {\n setSearchText(query);\n if (!query.trim()) {\n setCurrentPreviews([]);\n setCurrentSuggestions([]);\n setLastNext(null);\n return;\n }\n const [{ results: searchResults = [], next = null }, { results: suggestionResults = [] }] = await Promise.all([\n klipyService.search(query, { limit: 18, contentfilter: 'medium' }),\n klipyService.searchSuggestions(query),\n ]);\n setCurrentPreviews(searchResults);\n setLastNext(next);\n setCurrentSuggestions(suggestionResults);\n }\n\n async function handleLoadAdditional() {\n const { results: searchResults = [], next = null } = await klipyService.search(searchText, {\n limit: 36,\n pos: lastNext ?? undefined,\n contentfilter: 'medium',\n });\n setLastNext(next);\n setCurrentPreviews((prev) => [...prev, ...searchResults]);\n }\n\n async function handlePreviewSelect(preview: KlipyResult | null) {\n await onSelect(preview);\n setIsMenuOpen(false);\n if (preview) klipyService.registerShare(searchText, preview.id);\n }\n\n function renderSuggestions(): ReactNode {\n return (\n <ChipContainer inline className={styles.klipyPickerSuggestionsContainer}>\n {currentSuggestions.map((suggestion) => (\n <Chip\n className={styles.klipyPickerSuggestionsItem}\n key={suggestion}\n text={suggestion}\n onClick={() => searchFor(suggestion)}\n />\n ))}\n </ChipContainer>\n );\n }\n\n function renderPreviewPicker(): ReactNode {\n return (\n <GifPreview\n previewItems={currentPreviews}\n onSelect={handlePreviewSelect}\n onLoadAdditional={handleLoadAdditional}\n />\n );\n }\n\n function renderSearchInput(): ReactNode {\n return <SearchInput label=
|
|
1
|
+
{"version":3,"file":"klipyPicker.mjs","names":[],"sources":["../../../../package/common/klipyPicker/klipyPicker.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport cx from 'classnames';\nimport type { ReactElement, ReactNode } from 'react';\nimport KlipyService from 'services/klipy';\nimport type { KlipyResult } from 'services/klipy';\nimport SearchInput from '../searchInput';\nimport GifPreview from './components/gifPreview';\nimport FloatingMenu from '../floatingMenu';\nimport Button, { ButtonStyleType } from '../button';\nimport Modal from '../modal';\nimport Chip, { ChipContainer } from '../chip';\nimport GifView from './components/gifView';\nimport type { KlipyPickerLabels, KlipyPickerProps } from './klipyPicker.types';\nimport { useLabels } from '../configProvider';\n\nconst DEFAULT_LABELS: Required<KlipyPickerLabels> = {\n search: 'Pick gif',\n};\nimport styles from './klipyPicker.module.pcss';\nimport actionButtonStyles from 'components/common/actionButtons/actionButtons.module.pcss';\nimport { Align } from '../floatingMenu/floatingMenu.types';\n\nexport default function KlipyPicker({\n className,\n token = '',\n isModal = false,\n onSelect = () => undefined,\n selected = null,\n labels,\n}: KlipyPickerProps): ReactElement {\n const text = useLabels('KlipyPicker', labels, DEFAULT_LABELS);\n const klipyService = useMemo(() => new KlipyService(token), [token]);\n\n const [searchText, setSearchText] = useState('');\n const [currentPreviews, setCurrentPreviews] = useState<KlipyResult[]>([]);\n const [currentSuggestions, setCurrentSuggestions] = useState<string[]>([]);\n const [lastNext, setLastNext] = useState<string | null>(null);\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n\n async function searchFor(query: string) {\n setSearchText(query);\n if (!query.trim()) {\n setCurrentPreviews([]);\n setCurrentSuggestions([]);\n setLastNext(null);\n return;\n }\n const [{ results: searchResults = [], next = null }, { results: suggestionResults = [] }] = await Promise.all([\n klipyService.search(query, { limit: 18, contentfilter: 'medium' }),\n klipyService.searchSuggestions(query),\n ]);\n setCurrentPreviews(searchResults);\n setLastNext(next);\n setCurrentSuggestions(suggestionResults);\n }\n\n async function handleLoadAdditional() {\n const { results: searchResults = [], next = null } = await klipyService.search(searchText, {\n limit: 36,\n pos: lastNext ?? undefined,\n contentfilter: 'medium',\n });\n setLastNext(next);\n setCurrentPreviews((prev) => [...prev, ...searchResults]);\n }\n\n async function handlePreviewSelect(preview: KlipyResult | null) {\n await onSelect(preview);\n setIsMenuOpen(false);\n if (preview) klipyService.registerShare(searchText, preview.id);\n }\n\n function renderSuggestions(): ReactNode {\n return (\n <ChipContainer inline className={styles.klipyPickerSuggestionsContainer}>\n {currentSuggestions.map((suggestion) => (\n <Chip\n className={styles.klipyPickerSuggestionsItem}\n key={suggestion}\n text={suggestion}\n onClick={() => searchFor(suggestion)}\n />\n ))}\n </ChipContainer>\n );\n }\n\n function renderPreviewPicker(): ReactNode {\n return (\n <GifPreview\n previewItems={currentPreviews}\n onSelect={handlePreviewSelect}\n onLoadAdditional={handleLoadAdditional}\n />\n );\n }\n\n function renderSearchInput(): ReactNode {\n return <SearchInput label={text.search} value={searchText} onDebounce={searchFor} onChange={setSearchText} />;\n }\n\n if (isModal) {\n return (\n <div className={cx(styles.klipyPicker, className)}>\n <div className={actionButtonStyles.actionButtons}>\n <Button onClick={() => setIsMenuOpen(true)}>Pick gif</Button>\n {selected && (\n <Button styleType={ButtonStyleType.Delete} onClick={() => handlePreviewSelect(null)}>\n Remove gif\n </Button>\n )}\n </div>\n {selected && <GifView className={styles.klipyPickerSelected} src={selected} />}\n <Modal isOpen={isMenuOpen} onClose={() => setIsMenuOpen(false)}>\n <div className={styles.klipyPickerModalContent}>\n {renderSearchInput()}\n {renderSuggestions()}\n {renderPreviewPicker()}\n </div>\n </Modal>\n </div>\n );\n }\n\n return (\n <div className={cx(styles.klipyPicker, className)}>\n <FloatingMenu\n align={Align.Left}\n isOpen={currentPreviews.length > 0 && isMenuOpen}\n onOpenChange={setIsMenuOpen}\n dontCloseOnChildClick\n content={\n <>\n {renderSuggestions()}\n {renderPreviewPicker()}\n </>\n }\n >\n {renderSearchInput()}\n </FloatingMenu>\n {selected && (\n <Button styleType={ButtonStyleType.Delete} onClick={() => handlePreviewSelect(null)}>\n Remove gif\n </Button>\n )}\n {selected && <GifView className={styles.klipyPickerSelected} src={selected} />}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{usePresentationDefault as n}from"../configProvider/usePresentationDefault.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"../styles/sheet.module.mjs";import{useVisualViewportSize as a}from"../hooks/useVisualViewportSize.mjs";import o from"../hooks/useSheet.mjs";import{useOverlay as s}from"../hooks/useOverlay.mjs";import c from"./modal.module.mjs";import{useEffect as l,useId as u,useRef as d,useState as f}from"react";import p from"classnames";import{FontAwesomeIcon as m}from"@fortawesome/react-fontawesome";import{faXmark as h}from"@fortawesome/free-solid-svg-icons";import{jsx as g,jsxs as _}from"react/jsx-runtime";var v={close:`Close`};function y(y){let{children:b=null,title:x=``,isOpen:S=!1,onClose:C=()=>null,className:w=``,style:T,hideHeader:E=!1,closeOnEscape:D=!0,presentation:O,classNames:k,styles:A,labels:j,...M}=e(`Modal`,y),N=t(`Modal`,j,v),[P,F]=f(S),[I,L]=f(S),R=d(null),z=u(),{height:B,offsetTop:V}=a(),H=n(),{isSheet:U,sheetStyle:W}=o(O??H);if(l(()=>{S?(F(S),setTimeout(()=>L(!0),1)):(L(!1),setTimeout(()=>F(S),300))},[S]),s({isActive:P&&I,containerRef:R,onEscape:D?C:void 0}),!P)return null;let G=!E&&!!x;return g(r,{children:_(`div`,{className:p(c.modalContainer,k?.container),style:{height:B,top:V,...A?.container},children:[g(`div`,{className:p(c.modalWrapper,I&&c.modalWrapperShown,k?.backdrop),style:A?.backdrop,onClick:C}),I&&_(`div`,{...M,ref:R,role:`dialog`,"aria-modal":`true`,"aria-labelledby":G?z:void 0,tabIndex:-1,className:p(c.modal,I&&c.modalShown,U&&[i.sheet,i.sheetSurface,c.modalSheet],w,k?.modal),style:{...U?W:void 0,...T,...A?.modal},onClick:e=>e.stopPropagation(),children:[!E&&_(`div`,{className:p(c.modalHeader,k?.header),style:A?.header,children:[g(`div`,{id:z,className:k?.title,style:A?.title,children:x}),g(`button`,{type:`button`,onClick:C,"aria-label":N.close,className:p(c.modalCloseButton,k?.closeButton),style:A?.closeButton,children:g(m,{icon:h})})]}),g(`div`,{className:p(c.modalBody,k?.body),style:A?.body,children:b})]})]})})}export{y as default};
|
|
2
2
|
//# sourceMappingURL=modal.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useComponentDefaults, usePresentationDefault } from 'components/common/configProvider';\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title = '',\n isOpen = false,\n onClose = () => null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Modal', props);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(isOpen);\n setTimeout(() => setAnimateIn(true), 1);\n } else {\n setAnimateIn(false);\n setTimeout(() => setIsRendered(isOpen), 300);\n }\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n aria-labelledby={hasHeaderTitle ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <button\n type='button'\n onClick={onClose}\n aria-label=
|
|
1
|
+
{"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title = '',\n isOpen = false,\n onClose = () => null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Modal', props);\n const text = useLabels('Modal', labels, DEFAULT_LABELS);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(isOpen);\n setTimeout(() => setAnimateIn(true), 1);\n } else {\n setAnimateIn(false);\n setTimeout(() => setIsRendered(isOpen), 300);\n }\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n aria-labelledby={hasHeaderTitle ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <button\n type='button'\n onClick={onClose}\n aria-label={text.close}\n className={cx(styles.modalCloseButton, classNames?.closeButton)}\n style={slotStyles?.closeButton}\n >\n <FontAwesomeIcon icon={closeIcon} />\n </button>\n </div>\n )}\n <div className={cx(styles.modalBody, classNames?.body)} style={slotStyles?.body}>\n {children}\n </div>\n </div>\n )}\n </div>\n </BodyEnd>\n );\n}\n\nexport default Modal;\n"],"mappings":"2wBAeA,IAAM,EAAwC,CAC5C,MAAO,OACT,EAEA,SAAS,EAAM,EAAmB,CAChC,GAAM,CACJ,WAAW,KACX,QAAQ,GACR,SAAS,GACT,cAAgB,KAChB,YAAY,GACZ,QACA,aAAa,GACb,gBAAgB,GAChB,aAAc,EACd,aACA,OAAQ,EACR,SACA,GAAG,GACD,EAAqB,QAAS,CAAK,EACjC,EAAO,EAAU,QAAS,EAAQ,CAAc,EAChD,CAAC,EAAY,GAAiB,EAAS,CAAM,EAC7C,CAAC,EAAW,GAAgB,EAAS,CAAM,EAC3C,EAAW,EAAuB,IAAI,EACtC,EAAU,EAAM,EAGhB,CAAE,OAAQ,EAAgB,UAAW,GAAsB,EAAsB,EAGjF,EAAkB,EAAuB,EAEzC,CAAE,UAAS,cAAe,EADX,GAAoB,CACY,EAqBrD,GAnBA,MAAgB,CACV,GAEF,EAAc,CAAM,EACpB,eAAiB,EAAa,EAAI,EAAG,CAAC,IAEtC,EAAa,EAAK,EAClB,eAAiB,EAAc,CAAM,EAAG,GAAG,EAE/C,EAAG,CAAC,CAAM,CAAC,EAIX,EAAW,CACT,SAAU,GAAc,EACxB,aAAc,EACd,SAAU,EAAgB,EAAU,IAAA,EACtC,CAAC,EAEG,CAAC,EAAY,OAAO,KAExB,IAAM,EAAiB,CAAC,GAAc,CAAC,CAAC,EAExC,OACE,EAAC,EAAD,CAAA,SACE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,eAAgB,GAAY,SAAS,EAC1D,MAAO,CAAE,OAAQ,EAAgB,IAAK,EAAmB,GAAG,GAAY,SAAU,EAFpF,SAAA,CAIE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,aAAc,GAAa,EAAO,kBAAmB,GAAY,QAAQ,EAC9F,MAAO,GAAY,SACnB,QAAS,CACV,CAAA,EACA,GACC,EAAC,MAAD,CACE,GAAI,EACJ,IAAK,EACL,KAAK,SACL,aAAW,OACX,kBAAiB,EAAiB,EAAU,IAAA,GAE5C,SAAU,GACV,UAAW,EACT,EAAO,MACP,GAAa,EAAO,WAGpB,GAAW,CAAC,EAAY,MAAO,EAAY,aAAc,EAAO,UAAU,EAC1E,EACA,GAAY,KACd,EACA,MAAO,CAAE,GAAI,EAAU,EAAa,IAAA,GAAY,GAAG,EAAO,GAAG,GAAY,KAAM,EAC/E,QAAU,GAAM,EAAE,gBAAgB,EAlBpC,SAAA,CAoBG,CAAC,GACA,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,YAAa,GAAY,MAAM,EAAG,MAAO,GAAY,OAA/E,SAAA,CACE,EAAC,MAAD,CAAK,GAAI,EAAS,UAAW,GAAY,MAAO,MAAO,GAAY,MAChE,SAAA,CACE,CAAA,EACL,EAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,aAAY,EAAK,MACjB,UAAW,EAAG,EAAO,iBAAkB,GAAY,WAAW,EAC9D,MAAO,GAAY,YAEnB,SAAA,EAAC,EAAD,CAAiB,KAAM,CAAY,CAAA,CAC7B,CAAA,CACL,CAEP,CAAA,EAAA,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,UAAW,GAAY,IAAI,EAAG,MAAO,GAAY,KACxE,UACE,CAAA,CACF,CAEJ,CAAA,CAAA,CACE,CAAA,CAAA,CAAA,CAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import t from"../chip/chip.mjs";import
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../chip/chip.mjs";import r from"../tooltip/tooltip.mjs";import ee from"../searchInput/searchInput.mjs";import{PopoverAlign as te,PopoverPlacement as ne,PopoverTrigger as re}from"../popover/popover.types.mjs";import ie from"../popover/popover.mjs";import i from"./multiSelect.module.mjs";import{useEffect as a,useId as ae,useRef as o,useState as s}from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faCheck as oe,faChevronDown as se,faXmark as ce}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var le=[],ue={clearSelection:`Clear selection`,create:e=>`Add "${e}"`,searchPlaceholder:`Search…`,empty:`No matches`};function f(f){let{items:p,value:m,defaultValue:de=[],onChange:fe=()=>{},label:h=``,alwaysFloatLabel:pe=!1,placeholder:g=``,searchable:me=!1,allowCustomValues:_=!1,maxSelected:v,clearable:he=!0,disabled:y=!1,readOnly:b=!1,presentation:ge,formValidator:x=null,errorMessage:_e=``,isValid:ve=!0,className:ye,style:be,classNames:S,styles:C,onMouseEnter:xe,onMouseLeave:Se,onFocus:Ce,onBlur:we,labels:Te,...Ee}=e(`MultiSelect`,f),w=t(`MultiSelect`,Te,ue),T=o(null),E=o(null),D=o(null),O=ae(),[De,Oe]=s(de),[k,ke]=s(!1),[A,j]=s(``),[M,N]=s(0),[P,F]=s(!1),[Ae,I]=s(!1),[L,je]=s(),R=o(x),[,Me]=s(0);a(()=>{R.current=x;let e=R.current;if(!e)return;e.label=h,e.validate();let t=()=>Me(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[x,h]);let z=m!==void 0&&!x,B=x?x.value:z?m:De,V=Array.isArray(B)?B:le,H=x?x.touched&&x.hasError():!ve||!!_e,Ne=x?x.getCurrentErrorMessage():_e||``,U=!y&&!b,W=v!==void 0&&V.length>=v,G=pe||k||P||V.length>0,K=me||_,q=A.trim(),J=K?q.toLowerCase():``,Y=J?p.filter(e=>e.label.toLowerCase().includes(J)):p,Pe=_&&q.length>0&&!W&&!p.some(e=>e.label.toLowerCase()===J||String(e.key).toLowerCase()===J)&&!V.some(e=>String(e).toLowerCase()===J),X=Pe?[...Y,{key:q,label:w.create(q)}]:Y,Fe=V.map(e=>p.find(t=>t.key===e)??(_?{key:e,label:String(e)}:void 0)).filter(e=>e!==void 0);function Z(e){x?x.set(e):z||Oe(e),fe(e)}function Q(e){U&&!e.disabled&&(V.includes(e.key)?Z(V.filter(t=>t!==e.key)):W||Z([...V,e.key]),Pe&&e.key===q&&j(``))}function Ie(){T.current?.contains(document.activeElement)&&E.current?.focus()}function Le(e){(!e||U)&&(ke(e),e?(je(E.current?.offsetWidth),N(0)):(j(``),R.current&&(R.current.touched=!0)))}a(()=>{k&&K&&D.current?.querySelector(`input`)?.focus()},[k,K]);function $(e){if(U){if(!k){e.key===`ArrowDown`||e.key===`Enter`||e.key===` `?(e.preventDefault(),Le(!0)):e.key===`Backspace`&&V.length>0&&Z(V.slice(0,-1));return}if(e.key===`ArrowDown`||e.key===`ArrowUp`){if(e.preventDefault(),X.length===0)return;let t=e.key===`ArrowDown`?1:-1;N(e=>(e+t+X.length)%X.length)}else if(e.key===`Home`||e.key===`End`)e.preventDefault(),N(e.key===`Home`?0:X.length-1);else if(e.key===`Enter`){e.preventDefault();let t=X[M];t&&Q(t)}else e.key===`Backspace`&&!A&&V.length>0&&Z(V.slice(0,-1))}}let Re=d(`div`,{className:c(i.multiSelectPanel,S?.panel),style:C?.panel,children:[K&&u(`div`,{ref:D,className:c(i.multiSelectSearch,S?.search),style:C?.search,children:u(ee,{value:A,onChange:e=>{j(e),N(0)},placeholder:w.searchPlaceholder,onKeyDown:$})}),u(`div`,{id:O,role:`listbox`,"aria-multiselectable":`true`,"aria-label":h||void 0,className:c(i.multiSelectOptions,S?.options),style:C?.options,children:X.length===0?u(`div`,{className:c(i.multiSelectEmpty,S?.empty),style:C?.empty,children:w.empty}):X.map((e,t)=>{let n=V.includes(e.key),r=e.disabled||W&&!n;return d(`div`,{id:`${O}-${t}`,role:`option`,"aria-selected":n,"aria-disabled":r||void 0,className:c(i.multiSelectOption,t===M&&i.multiSelectOptionActive,n&&i.multiSelectOptionSelected,r&&i.multiSelectOptionDisabled,S?.option),style:C?.option,onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>N(t),onClick:()=>Q(e),children:[u(`span`,{className:c(i.multiSelectOptionCheck,S?.optionCheck),style:C?.optionCheck,children:n&&u(l,{icon:oe})}),u(`span`,{className:c(i.multiSelectOptionLabel,S?.optionLabel),style:C?.optionLabel,children:e.label})]},e.key)})})]});return d(`div`,{...Ee,ref:T,className:c(i.multiSelect,y&&i.multiSelectDisabled,S?.root,ye),style:{...be,...C?.root},onMouseEnter:e=>{I(!0),xe?.(e)},onMouseLeave:e=>{I(!1),Se?.(e)},onFocus:e=>{F(!0),Ce?.(e)},onBlur:e=>{e.currentTarget.contains(e.relatedTarget)||(F(!1),we?.(e))},children:[u(ie,{isOpen:k,onOpenChange:Le,trigger:re.Click,placement:ne.Bottom,align:te.Start,offset:4,withArrow:!1,disabled:!U,presentation:ge,content:Re,className:i.multiSelectPopover,styles:{panel:L?{minWidth:L}:void 0},children:d(`div`,{ref:E,role:`combobox`,tabIndex:y?-1:0,"aria-expanded":k,"aria-haspopup":`listbox`,"aria-controls":O,"aria-activedescendant":k&&X.length>0?`${O}-${M}`:void 0,"aria-disabled":y||void 0,"aria-readonly":b||void 0,className:c(i.multiSelectControl,S?.control),style:C?.control,onKeyDown:$,children:[g&&Fe.length===0&&u(`span`,{className:c(i.multiSelectPlaceholder,(!h||G)&&i.multiSelectPlaceholderShown,!h&&i.multiSelectPlaceholderInstant,S?.placeholder),style:C?.placeholder,children:g}),u(`div`,{className:c(i.multiSelectValues,S?.values),style:C?.values,children:Fe.map(e=>u(n,{text:e.label,showRemoveIcon:U,labels:{remove:`Remove ${e.label}`},onMouseDown:e=>e.preventDefault(),onRemove:()=>{Q(e),Ie()},className:c(i.multiSelectChip,S?.chip),style:C?.chip},e.key))}),he&&U&&V.length>0&&u(`button`,{type:`button`,tabIndex:-1,"aria-label":w.clearSelection,className:c(i.multiSelectClearButton,S?.clearButton),style:C?.clearButton,onMouseDown:e=>e.preventDefault(),onClick:e=>{e.stopPropagation(),Z([]),Ie()},children:u(l,{icon:ce})}),u(l,{icon:se,className:c(i.multiSelectIcon,k&&i.multiSelectIconOpen,S?.icon),style:C?.icon}),h&&u(`div`,{className:c(i.multiSelectLabel,G&&i.multiSelectLabelFloating,S?.label),style:C?.label,children:h}),u(`fieldset`,{className:c(i.multiSelectFieldset,!h&&i.multiSelectFieldsetNoNotch,(P||k)&&i.multiSelectFieldsetFocused,H&&i.multiSelectFieldsetError,S?.fieldset),style:C?.fieldset,children:u(`legend`,{className:c(i.multiSelectFieldsetLegend,G&&i.multiSelectFieldsetLegendActive),children:u(`span`,{children:h})})})]})}),u(r,{variant:`error`,message:Ne,isVisible:H&&(P||Ae)})]})}export{f as default};
|
|
2
2
|
//# sourceMappingURL=multiSelect.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults } from '../configProvider';\nimport type { MultiSelectItem, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n createLabel = (value: string) => `Add \"${value}\"`,\n searchPlaceholder = 'Search…',\n emptyLabel = 'No matches',\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: createLabel(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n if (visibleItems.length === 0) return;\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex((current) => (current + step + visibleItems.length) % visibleItems.length);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(event.key === 'Home' ? 0 : visibleItems.length - 1);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {emptyLabel}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(styles.multiSelect, disabled && styles.multiSelectDisabled, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n className={styles.multiSelectPopover}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n removeLabel={`Remove ${item.label}`}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label='Clear selection'\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n !label && styles.multiSelectFieldsetNoNotch,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"krBAgBA,IAAM,GAA4B,CAAC,EAEnC,SAAS,EAAY,EAAuC,CAC1D,GAAM,CACJ,QACA,QACA,gBAAe,CAAC,EAChB,gBAAiB,CAAC,EAClB,QAAQ,GACR,oBAAmB,GACnB,cAAc,GACd,cAAa,GACb,oBAAoB,GACpB,eAAe,GAAkB,QAAQ,EAAM,GAC/C,qBAAoB,UACpB,cAAa,aACb,cACA,aAAY,GACZ,WAAW,GACX,YAAW,GACX,gBACA,gBAAgB,KAChB,eAAe,GACf,WAAU,GACV,aACA,SACA,aACA,OAAQ,EACR,gBACA,gBACA,WACA,UACA,GAAG,IACD,EAAqB,cAAe,CAAK,EAEvC,EAAU,EAAuB,IAAI,EACrC,EAAa,EAAuB,IAAI,EACxC,EAAY,EAAuB,IAAI,EACvC,EAAY,GAAM,EAElB,CAAC,GAAe,IAAoB,EAA6B,EAAY,EAC7E,CAAC,EAAQ,IAAa,EAAS,EAAK,EACpC,CAAC,EAAQ,GAAa,EAAS,EAAE,EACjC,CAAC,EAAa,GAAkB,EAAS,CAAC,EAC1C,CAAC,EAAW,GAAgB,EAAS,EAAK,EAC1C,CAAC,GAAW,GAAgB,EAAS,EAAK,EAC1C,CAAC,EAAY,IAAiB,EAAiB,EAI/C,EAAQ,EAAO,CAAa,EAC5B,EAAG,IAAe,EAAS,CAAC,EAClC,MAAgB,CACd,EAAM,QAAU,EAChB,IAAM,EAAK,EAAM,QACjB,GAAI,CAAC,EAAI,OACT,EAAG,MAAQ,EACX,EAAG,SAAS,EACZ,IAAM,MAAiB,GAAa,GAAM,EAAI,CAAC,EAE/C,OADA,EAAG,yBAAyB,CAAQ,MACvB,CACX,EAAG,uBAAuB,CAAQ,CACpC,CACF,EAAG,CAAC,EAAe,CAAK,CAAC,EAEzB,IAAM,EAAe,IAAU,IAAA,IAAa,CAAC,EACvC,EAAW,EAAgB,EAAc,MAAQ,EAAe,EAAQ,GACxE,EAAW,MAAM,QAAQ,CAAQ,EAAK,EAAkC,GAExE,EAAW,EAAgB,EAAc,SAAW,EAAc,SAAS,EAAI,CAAC,IAAW,CAAC,CAAC,EAC7F,GAAY,EAAgB,EAAc,uBAAuB,EAAI,GAAgB,GAErF,EAAgB,CAAC,GAAY,CAAC,GAC9B,EAAQ,IAAgB,IAAA,IAAa,EAAS,QAAU,EAExD,EAAmB,IAAoB,GAAU,GAAa,EAAS,OAAS,EAEhF,EAAe,IAAc,EAC7B,EAAQ,EAAO,KAAK,EACpB,EAAS,EAAe,EAAM,YAAY,EAAI,GAC9C,EAAU,EAAS,EAAM,OAAQ,GAAS,EAAK,MAAM,YAAY,CAAC,CAAC,SAAS,CAAM,CAAC,EAAI,EAEvF,EACJ,GACA,EAAM,OAAS,GACf,CAAC,GACD,CAAC,EAAM,KAAM,GAAS,EAAK,MAAM,YAAY,IAAM,GAAU,OAAO,EAAK,GAAG,CAAC,CAAC,YAAY,IAAM,CAAM,GACtG,CAAC,EAAS,KAAM,GAAQ,OAAO,CAAG,CAAC,CAAC,YAAY,IAAM,CAAM,EAExD,EAAkC,EAAY,CAAC,GAAG,EAAS,CAAE,IAAK,EAAO,MAAO,GAAY,CAAK,CAAE,CAAC,EAAI,EAIxG,GAAgB,EACnB,IACE,GAAQ,EAAM,KAAM,GAAS,EAAK,MAAQ,CAAG,IAAM,EAAoB,CAAE,MAAK,MAAO,OAAO,CAAG,CAAE,EAAI,IAAA,GACxG,CAAC,CACA,OAAQ,GAAkC,IAAS,IAAA,EAAS,EAE/D,SAAS,EAAO,EAA0B,CACpC,EAAe,EAAc,IAAI,CAAI,EAC/B,GAAc,GAAiB,CAAI,EAC7C,GAAS,CAAI,CACf,CAEA,SAAS,EAAO,EAAuB,CAChC,GAAiB,GAAK,WACvB,EAAS,SAAS,EAAK,GAAG,EAAG,EAAO,EAAS,OAAQ,GAAQ,IAAQ,EAAK,GAAG,CAAC,EACxE,GAAO,EAAO,CAAC,GAAG,EAAU,EAAK,GAAG,CAAC,EAE3C,GAAa,EAAK,MAAQ,GAAO,EAAU,EAAE,EACnD,CAOA,SAAS,IAAqB,CACxB,EAAQ,SAAS,SAAS,SAAS,aAAa,GAAG,EAAW,SAAS,MAAM,CACnF,CAEA,SAAS,GAAiB,EAAe,EACnC,IAAS,KACb,GAAU,CAAI,EACV,GACF,GAAc,EAAW,SAAS,WAAW,EAC7C,EAAe,CAAC,IAEhB,EAAU,EAAE,EACR,EAAM,UAAS,EAAM,QAAQ,QAAU,KAE/C,CAEA,MAAgB,CACT,GAAW,GAChB,EAAU,SAAS,cAAc,OAAO,CAAC,EAAE,MAAM,CACnD,EAAG,CAAC,EAAQ,CAAY,CAAC,EAEzB,SAAS,EAAc,EAA4B,CAC5C,KAEL,IAAI,CAAC,EAAQ,CACP,EAAM,MAAQ,aAAe,EAAM,MAAQ,SAAW,EAAM,MAAQ,KACtE,EAAM,eAAe,EACrB,GAAiB,EAAI,GACZ,EAAM,MAAQ,aAAe,EAAS,OAAS,GACxD,EAAO,EAAS,MAAM,EAAG,EAAE,CAAC,EAE9B,MACF,CAEA,GAAI,EAAM,MAAQ,aAAe,EAAM,MAAQ,UAAW,CAExD,GADA,EAAM,eAAe,EACjB,EAAa,SAAW,EAAG,OAC/B,IAAM,EAAO,EAAM,MAAQ,YAAc,EAAI,GAC7C,EAAgB,IAAa,EAAU,EAAO,EAAa,QAAU,EAAa,MAAM,CAC1F,MAAO,GAAI,EAAM,MAAQ,QAAU,EAAM,MAAQ,MAC/C,EAAM,eAAe,EACrB,EAAe,EAAM,MAAQ,OAAS,EAAI,EAAa,OAAS,CAAC,OAC5D,GAAI,EAAM,MAAQ,QAAS,CAChC,EAAM,eAAe,EACrB,IAAM,EAAO,EAAa,GACtB,GAAM,EAAO,CAAI,CACvB,MAAW,EAAM,MAAQ,aAAe,CAAC,GAAU,EAAS,OAAS,GACnE,EAAO,EAAS,MAAM,EAAG,EAAE,CAAC,CAf9B,CAiBF,CAEA,IAAM,GACJ,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,iBAAkB,GAAY,KAAK,EAAG,MAAO,GAAY,MAAnF,SAAA,CACG,GACC,EAAC,MAAD,CAAK,IAAK,EAAW,UAAW,EAAG,EAAO,kBAAmB,GAAY,MAAM,EAAG,MAAO,GAAY,OACnG,SAAA,EAAC,EAAD,CACE,MAAO,EACP,SAAW,GAAU,CACnB,EAAU,CAAK,EACf,EAAe,CAAC,CAClB,EACA,YAAa,GACb,UAAW,CACZ,CAAA,CACE,CAAA,EAEP,EAAC,MAAD,CACE,GAAI,EACJ,KAAK,UACL,uBAAqB,OACrB,aAAY,GAAS,IAAA,GACrB,UAAW,EAAG,EAAO,mBAAoB,GAAY,OAAO,EAC5D,MAAO,GAAY,QAElB,SAAA,EAAa,SAAW,EACvB,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,iBAAkB,GAAY,KAAK,EAAG,MAAO,GAAY,MAChF,SAAA,EACE,CAAA,EAEL,EAAa,KAAK,EAAM,IAAU,CAChC,IAAM,EAAa,EAAS,SAAS,EAAK,GAAG,EACvC,EAAa,EAAK,UAAa,GAAS,CAAC,EAE/C,OACE,EAAC,MAAD,CAEE,GAAI,GAAG,EAAU,GAAG,IACpB,KAAK,SACL,gBAAe,EACf,gBAAe,GAAc,IAAA,GAC7B,UAAW,EACT,EAAO,kBACP,IAAU,GAAe,EAAO,wBAChC,GAAc,EAAO,0BACrB,GAAc,EAAO,0BACrB,GAAY,MACd,EACA,MAAO,GAAY,OACnB,YAAc,GAAU,EAAM,eAAe,EAC7C,iBAAoB,EAAe,CAAK,EACxC,YAAe,EAAO,CAAI,EAhB5B,SAAA,CAkBE,EAAC,OAAD,CACE,UAAW,EAAG,EAAO,uBAAwB,GAAY,WAAW,EACpE,MAAO,GAAY,YAElB,SAAA,GAAc,EAAC,EAAD,CAAiB,KAAM,EAAU,CAAA,CAC5C,CAAA,EACN,EAAC,OAAD,CACE,UAAW,EAAG,EAAO,uBAAwB,GAAY,WAAW,EACpE,MAAO,GAAY,YAElB,SAAA,EAAK,KACF,CAAA,CACH,CA7BE,EAAA,EAAK,GA6BP,CAET,CAAC,CAEA,CAAA,CACF,IAGP,OACE,EAAC,MAAD,CACE,GAAI,GACJ,IAAK,EACL,UAAW,EAAG,EAAO,YAAa,GAAY,EAAO,oBAAqB,GAAY,KAAM,EAAS,EACrG,MAAO,CAAE,GAAG,GAAO,GAAG,GAAY,IAAK,EACvC,aAAe,GAAU,CACvB,EAAa,EAAI,EACjB,KAAe,CAAK,CACtB,EACA,aAAe,GAAU,CACvB,EAAa,EAAK,EAClB,KAAe,CAAK,CACtB,EACA,QAAU,GAAU,CAClB,EAAa,EAAI,EACjB,KAAU,CAAK,CACjB,EACA,OAAS,GAAU,CACb,EAAM,cAAc,SAAS,EAAM,aAA4B,IACnE,EAAa,EAAK,EAClB,KAAS,CAAK,EAChB,EArBF,SAAA,CAuBE,EAAC,GAAD,CACU,SACR,aAAc,GACd,QAAS,GAAe,MACxB,UAAW,GAAiB,OAC5B,MAAO,GAAa,MACpB,OAAQ,EACR,UAAW,GACX,SAAU,CAAC,EACG,gBACd,QAAS,GACT,UAAW,EAAO,mBAClB,OAAQ,CAAE,MAAO,EAAa,CAAE,SAAU,CAAW,EAAI,IAAA,EAAU,EAEnE,SAAA,EAAC,MAAD,CACE,IAAK,EACL,KAAK,WACL,SAAU,EAAW,GAAK,EAC1B,gBAAe,EACf,gBAAc,UACd,gBAAe,EACf,wBAAuB,GAAU,EAAa,OAAS,EAAI,GAAG,EAAU,GAAG,IAAgB,IAAA,GAC3F,gBAAe,GAAY,IAAA,GAC3B,gBAAe,IAAY,IAAA,GAC3B,UAAW,EAAG,EAAO,mBAAoB,GAAY,OAAO,EAC5D,MAAO,GAAY,QACnB,UAAW,EAZb,SAAA,CAcG,GAAe,GAAc,SAAW,GACvC,EAAC,OAAD,CACE,UAAW,EACT,EAAO,wBACN,CAAC,GAAS,IAAqB,EAAO,4BACvC,CAAC,GAAS,EAAO,8BACjB,GAAY,WACd,EACA,MAAO,GAAY,YAElB,SAAA,CACG,CAAA,EAGR,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,kBAAmB,GAAY,MAAM,EAAG,MAAO,GAAY,OAClF,SAAA,GAAc,IAAK,GAClB,EAAC,EAAD,CAEE,KAAM,EAAK,MACX,eAAgB,EAChB,YAAa,UAAU,EAAK,QAC5B,YAAc,GAAU,EAAM,eAAe,EAC7C,aAAgB,CACd,EAAO,CAAI,EACX,GAAmB,CACrB,EACA,UAAW,EAAG,EAAO,gBAAiB,GAAY,IAAI,EACtD,MAAO,GAAY,IACpB,EAXM,EAAK,GAWX,CACF,CACE,CAAA,EAEJ,IAAa,GAAiB,EAAS,OAAS,GAC/C,EAAC,SAAD,CACE,KAAK,SACL,SAAU,GACV,aAAW,kBACX,UAAW,EAAG,EAAO,uBAAwB,GAAY,WAAW,EACpE,MAAO,GAAY,YACnB,YAAc,GAAU,EAAM,eAAe,EAC7C,QAAU,GAAU,CAClB,EAAM,gBAAgB,EACtB,EAAO,CAAC,CAAC,EACT,GAAmB,CACrB,EAEA,SAAA,EAAC,EAAD,CAAiB,KAAM,EAAU,CAAA,CAC3B,CAAA,EAGV,EAAC,EAAD,CACE,KAAM,GACN,UAAW,EAAG,EAAO,gBAAiB,GAAU,EAAO,oBAAqB,GAAY,IAAI,EAC5F,MAAO,GAAY,IACpB,CAAA,EAEA,GACC,EAAC,MAAD,CACE,UAAW,EACT,EAAO,iBACP,GAAoB,EAAO,yBAC3B,GAAY,KACd,EACA,MAAO,GAAY,MAElB,SAAA,CACE,CAAA,EAKP,EAAC,WAAD,CACE,UAAW,EACT,EAAO,oBACP,CAAC,GAAS,EAAO,4BAChB,GAAa,IAAW,EAAO,2BAChC,GAAY,EAAO,yBACnB,GAAY,QACd,EACA,MAAO,GAAY,SAEnB,SAAA,EAAC,SAAD,CACE,UAAW,EACT,EAAO,0BACP,GAAoB,EAAO,+BAC7B,EAEA,SAAA,EAAC,OAAD,CAAA,SAAO,CAAY,CAAA,CACb,CAAA,CACA,CAAA,CACP,GACE,CAAA,EAET,EAAC,EAAD,CAAS,QAAQ,QAAQ,QAAS,GAAW,UAAW,IAAa,GAAa,GAAa,CAAA,CAC5F,GAET"}
|
|
1
|
+
{"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n clearSelection: 'Clear selection',\n create: (value) => `Add \"${value}\"`,\n searchPlaceholder: 'Search…',\n empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n labels,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n if (visibleItems.length === 0) return;\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex((current) => (current + step + visibleItems.length) % visibleItems.length);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(event.key === 'Home' ? 0 : visibleItems.length - 1);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={text.searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {text.empty}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(styles.multiSelect, disabled && styles.multiSelectDisabled, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n className={styles.multiSelectPopover}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n labels={{ remove: `Remove ${item.label}` }}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label={text.clearSelection}\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n !label && styles.multiSelectFieldsetNoNotch,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../hooks/useCoarsePointer.mjs";import ee from"../buttonGroup/buttonGroup.mjs";import a from"../input/input.mjs";import o from"./numberInput.module.mjs";import te from"./useStepRepeat.mjs";import s from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faChevronDown as ne,faChevronUp as re,faMinus as ie,faPlus as ae}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";import{NumericFormat as oe}from"react-number-format";var se={increase:`Increase`,decrease:`Decrease`};function f({label:e,disabled:t,direction:n,onStart:r,onStop:i}){return{tabIndex:-1,disabled:t,"aria-label":e,onPointerDown:e=>{(e.button===0||e.pointerType!==`mouse`)&&(e.preventDefault(),r(n,e.shiftKey?10:1))},onPointerUp:i,onPointerCancel:i,onContextMenu:e=>e.preventDefault()}}function p(e){return u(`button`,{type:`button`,...f(e),className:c(o.arrowBtn,e.className),style:e.style,children:u(l,{icon:e.icon,"aria-hidden":`true`})})}function m(e){let{disabled:t,...i}=f(e);return u(r,{...i,className:c(o.splitBtn,e.className),style:e.style,size:n.Icon,disabled:t,icon:e.icon})}function h(n){let r=s.useRef(void 0),{value:l,onChange:f,formValidator:h,label:ce,error:g,className:_,style:v,classNames:y,styles:b,allowNegative:x=!1,decimalSeparator:S,thousandSeparator:C,prefix:le,suffix:ue,decimalScale:w,fixedDecimalScale:de=!0,placeholder:fe,sign:T,showArrows:pe=!1,step:E=1,min:D,max:O,arrowsLayout:k=`auto`,labels:me,readOnly:A=!1,disabled:j=!1,isRequired:he=!1,onKeyDown:M,...ge}=e(`NumberInput`,n),N=t(`NumberInput`,me,se),[,P]=s.useState(0);s.useEffect(()=>{if(r.current=h,!h)return;let e=()=>P(e=>e+1);return h.registerOnUpdateListener(e),()=>{h.removeOnUpdateListener(e)}},[h]);let F=he||!!h?.validators.find(e=>e.validatorId===`required`),I=h==null?l:h.value,L=T!=null&&I!=null?Math.abs(Number(I)):I,R=L==null?``:String(L);function z(e){let t=e;t!==void 0&&(T===`-`&&(t=-Math.abs(t)),T===`+`&&(t=Math.abs(t))),h?.set(t??null),f?.(t)}function B(e){let t=e;return!x&&T!==`-`&&(t=Math.max(0,t)),T===`-`&&(t=Math.min(0,t)),D!==void 0&&(t=Math.max(D,t)),O!==void 0&&(t=Math.min(O,t)),t}let V=I!=null&&I!==``?Number(I):0;function H(){if(A||j||I==null||I===``)return;let e=Number(I);if(!Number.isFinite(e))return;let t=B(e);t!==e&&z(t)}function U(e){if(A||j)return!1;let t=B(V+e);return t!==V&&(z(t),!0)}let{start:_e,stop:ve}=te(e=>U(e*E));function ye(e){if(e.key===`Enter`){H(),M?.(e);return}if(!G){M?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),U(e.shiftKey?E*10:E);return}if(e.key===`ArrowDown`){e.preventDefault(),U(e.shiftKey?-E*10:-E);return}M?.(e)}let be=T===`-`?`-`:T===`+`?`+`:le,W=i(),G=pe&&!A&&!j,K=k===`split`||k===`auto`&&W,q=B(V+E)!==V,J=B(V-E)!==V,Y={onStart:_e,onStop:ve},X=G&&!K?d(`div`,{className:c(o.arrows,y?.arrows),style:b?.arrows,children:[u(p,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:re,disabled:!q,direction:1,...Y}),u(p,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ne,disabled:!J,direction:-1,...Y})]}):null,xe=G?{role:`spinbutton`,"aria-valuenow":Number.isFinite(V)?V:void 0,"aria-valuemin":D,"aria-valuemax":O,"aria-valuetext":R||void 0}:{},Z=h?!h.touched||!h.hasError():!g,Q=G&&K,$=u(a,{label:ce,className:c(o.numberInput,{[o.numberInputSplit]:Q},Q?void 0:_,Q?void 0:y?.root,y?.input),style:{...Q?void 0:v,...Q?void 0:b?.root,...b?.input},value:R,onChange:()=>{},formValidator:null,isRequired:F,onBlur:()=>{H(),r.current&&(r.current.touched=!0)},isValid:Z,errorMessage:h?h.touched?h.getCurrentErrorMessage()??``:``:g??``,placeholder:fe,readOnly:A,disabled:j,suffix:X,classNames:X?{suffix:o.arrowSuffix}:void 0,inputMode:w===0?`numeric`:`decimal`,customInput:u(oe,{onValueChange:e=>z(e.floatValue),allowNegative:T==null&&x,decimalSeparator:S,thousandSeparator:C,decimalScale:w,fixedDecimalScale:de,prefix:be,suffix:ue,onKeyDown:ye,...xe,...ge})});return Q?d(ee,{className:c(o.split,{[o.splitInvalid]:!Z},_,y?.root),style:{...v,...b?.root},children:[u(m,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ie,disabled:!J,direction:-1,...Y}),$,u(m,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:ae,disabled:!q,direction:1,...Y})]}):$}export{h as default};
|
|
2
2
|
//# sourceMappingURL=numberInput.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputProps } from './numberInput.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button type='button' {...stepButtonProps(props)} className={styles.arrowBtn}>\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button {...handlers} className={styles.splitBtn} size={ButtonSize.Icon} disabled={disabled} icon={props.icon} />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n increaseLabel = 'Increase',\n decreaseLabel = 'Decrease',\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={styles.arrows}>\n <ChevronStepButton\n label={increaseLabel}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n label={decreaseLabel}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n isSplitGroup ? undefined : className,\n )}\n style={isSplitGroup ? undefined : style}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className)} style={style}>\n <SplitStepButton label={decreaseLabel} icon={faMinus} disabled={!canDecrease} direction={-1} {...stepperProps} />\n {input}\n <SplitStepButton label={increaseLabel} icon={faPlus} disabled={!canIncrease} direction={1} {...stepperProps} />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { CSSProperties, ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n className?: string;\n style?: CSSProperties;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n increase: 'Increase',\n decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button\n type='button'\n {...stepButtonProps(props)}\n className={cx(styles.arrowBtn, props.className)}\n style={props.style}\n >\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button\n {...handlers}\n className={cx(styles.splitBtn, props.className)}\n style={props.style}\n size={ButtonSize.Icon}\n disabled={disabled}\n icon={props.icon}\n />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n classNames,\n styles: slotStyles,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n labels,\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={cx(styles.arrows, classNames?.arrows)} style={slotStyles?.arrows}>\n <ChevronStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n // In split mode the ButtonGroup around all three is the root, so the\n // consumer's root class belongs there rather than on the field.\n isSplitGroup ? undefined : className,\n isSplitGroup ? undefined : classNames?.root,\n classNames?.input,\n )}\n style={{\n ...(isSplitGroup ? undefined : style),\n ...(isSplitGroup ? undefined : slotStyles?.root),\n ...slotStyles?.input,\n }}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup\n className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <SplitStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faMinus}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n {input}\n <SplitStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faPlus}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"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"}
|