@ahrowe/ui 0.13.0 → 0.14.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/animatedIcon/animatedIcon.mjs +2 -0
- package/dist/esm/common/animatedIcon/animatedIcon.mjs.map +1 -0
- package/dist/esm/common/animatedIcon/animatedIcon.module.mjs +2 -0
- package/dist/esm/common/animatedIcon/animatedIcon.module.mjs.map +1 -0
- package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
- package/dist/esm/common/buttonGroup/buttonGroup.mjs +2 -0
- package/dist/esm/common/buttonGroup/buttonGroup.mjs.map +1 -0
- package/dist/esm/common/buttonGroup/buttonGroup.module.mjs +2 -0
- package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -0
- package/dist/esm/common/buttonGroup/buttonGroup.types.mjs +2 -0
- package/dist/esm/common/buttonGroup/buttonGroup.types.mjs.map +1 -0
- package/dist/esm/common/divider/divider.mjs +1 -1
- package/dist/esm/common/divider/divider.mjs.map +1 -1
- package/dist/esm/common/divider/divider.module.mjs +1 -1
- package/dist/esm/common/divider/divider.module.mjs.map +1 -1
- package/dist/esm/common/divider/divider.types.mjs +1 -1
- package/dist/esm/common/divider/divider.types.mjs.map +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.module.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/floatingMenu/floatingMenu.mjs +1 -1
- package/dist/esm/common/floatingMenu/floatingMenu.mjs.map +1 -1
- package/dist/esm/common/floatingMenu/floatingMenu.module.mjs.map +1 -1
- package/dist/esm/common/hooks/useFocusBoundary.mjs +2 -0
- package/dist/esm/common/hooks/useFocusBoundary.mjs.map +1 -0
- package/dist/esm/common/input/input.mjs +1 -1
- package/dist/esm/common/input/input.mjs.map +1 -1
- package/dist/esm/common/input/input.module.mjs +1 -1
- package/dist/esm/common/input/input.module.mjs.map +1 -1
- package/dist/esm/common/input/input.types.mjs +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/numberInput/numberInput.mjs +1 -1
- package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
- package/dist/esm/common/popover/popover.mjs +1 -1
- package/dist/esm/common/popover/popover.mjs.map +1 -1
- package/dist/esm/common/splitButton/splitButton.mjs +2 -0
- package/dist/esm/common/splitButton/splitButton.mjs.map +1 -0
- package/dist/esm/common/splitButton/splitButton.module.mjs +2 -0
- package/dist/esm/common/splitButton/splitButton.module.mjs.map +1 -0
- package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
- package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
- package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs +1 -1
- package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
- package/dist/esm/common/virtualList/virtualRow.mjs +1 -1
- package/dist/esm/common/virtualList/virtualRow.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/package/common/animatedIcon/animatedIcon.d.ts +3 -0
- package/dist/types/package/common/animatedIcon/animatedIcon.types.d.ts +25 -0
- package/dist/types/package/common/animatedIcon/index.d.ts +2 -0
- package/dist/types/package/common/buttonGroup/buttonGroup.d.ts +4 -0
- package/dist/types/package/common/buttonGroup/buttonGroup.types.d.ts +12 -0
- package/dist/types/package/common/buttonGroup/index.d.ts +2 -0
- package/dist/types/package/common/configProvider/configProvider.types.d.ts +2 -0
- package/dist/types/package/common/divider/divider.types.d.ts +5 -0
- package/dist/types/package/common/hooks/useFocusBoundary.d.ts +20 -0
- package/dist/types/package/common/input/input.types.d.ts +5 -0
- package/dist/types/package/common/splitButton/index.d.ts +2 -0
- package/dist/types/package/common/splitButton/splitButton.d.ts +3 -0
- package/dist/types/package/common/splitButton/splitButton.types.d.ts +28 -0
- package/dist/types/package/common/themeProvider/theme.types.d.ts +1 -0
- package/dist/types/package/common/virtualList/useRowDrag.d.ts +4 -0
- package/dist/types/package/common/virtualList/useVirtualWindow.d.ts +3 -1
- package/dist/types/package/common/virtualList/virtualList.types.d.ts +5 -1
- package/dist/types/package/common/virtualList/virtualRow.d.ts +8 -1
- package/dist/types/package/index.d.ts +4 -0
- package/docs/ButtonGroup.md +90 -0
- package/docs/CLAUDE.md +2 -0
- package/docs/Divider.md +15 -3
- package/docs/FloatingMenu.md +7 -3
- package/docs/Input.md +30 -6
- package/docs/NumberInput.md +2 -0
- package/docs/SearchInput.md +6 -0
- package/docs/SplitButton.md +79 -0
- package/docs/VirtualList.md +86 -3
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.module.mjs","names":[],"sources":["../../../../package/common/input/input.module.pcss"],"sourcesContent":["\n.required-mark {\n padding: 0px 0px 0px 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.input {\n position: relative;\n width: 100%;\n --input-notch-height: 1.75em;\n\n &-placeholder {\n position: absolute;\n top: 50%;\n left: 7px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n user-select: none;\n pointer-events: none;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n right: 0;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n padding: 0;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n inset: calc(var(--input-notch-height) / -2) 0 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-legend {\n display: block;\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--input-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0.000001px;\n }\n }\n }\n\n &-container {\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n background-color: var(--input-background);\n transition: border-color;\n cursor: text;\n height: 100%;\n\n &-multiline {\n overflow: hidden;\n padding-top: 10px;\n align-items: flex-start;\n }\n\n &-border {\n outline: 1px solid var(--border-color);\n transition: border-color;\n background-color: var(--input-background);\n outline: none;\n\n &-focused {\n outline-color: var(--primary-color);\n border-color: var(--primary-color);\n }\n\n &-searchFocused {\n outline-color: var(--search-input-color);\n outline-width: 2px;\n border-color: var(--search-input-color);\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n }\n\n &-label {\n display: flex;\n color: var(--text-color);\n height: 100%;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n white-space: nowrap;\n margin-right: 5px;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n color: color(var(--border-color) lightness(40%));\n background-color: transparent;\n font-size: 12px;\n position: absolute;\n top: -15px;\n white-space: nowrap;\n }\n }\n\n &-upper-right-label {\n position: absolute;\n transform: translateY(calc(-100% - 1.5px));\n right: 8px;\n min-height: 12px;\n font-size: 12px;\n color: color(var(--border-color) lightness(40%));\n }\n\n &-field {\n appearance: none;\n -webkit-appearance: none;\n font-family: inherit;\n font-size: inherit;\n box-sizing: border-box;\n resize: none;\n width: 100%;\n border: none;\n border-radius: var(--default-border-radius);\n overflow: hidden;\n line-height: 1.25;\n color: var(--text-color);\n background: transparent;\n\n &:focus {\n outline: none;\n }\n\n &::placeholder {\n color: var(--text-light);\n }\n\n &-withIcon {\n padding-right:
|
|
1
|
+
{"version":3,"file":"input.module.mjs","names":[],"sources":["../../../../package/common/input/input.module.pcss"],"sourcesContent":["\n.required-mark {\n padding: 0px 0px 0px 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.input {\n position: relative;\n width: 100%;\n --input-notch-height: 1.75em;\n --input-icon-size: 1.2em;\n --input-icon-offset: 0.6em;\n --input-icon-gutter: 2.2em;\n\n &-placeholder {\n position: absolute;\n top: 50%;\n left: 7px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n user-select: none;\n pointer-events: none;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n right: 0;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n\n &-multiline {\n top: 14px;\n transform: translateY(0);\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n padding: 0;\n min-width: 0;\n box-sizing: border-box;\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n inset: calc(var(--input-notch-height) / -2) 0 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-no-notch {\n /* Only Firefox actually renders the reserved notch space as a visible overlap on\n label-less inputs (e.g. crossing a divider directly above it in a tightly-packed\n layout like the sidebar search box); Chrome's rendering was never broken, just\n consistently sized with every other input. @supports (-moz-appearance: none) is a\n reliable Firefox-only feature query, so this scopes the fix to where it's needed\n instead of changing every browser's sizing. */\n @supports (-moz-appearance: none) {\n inset: 0;\n }\n }\n\n &-legend {\n display: block;\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--input-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0.000001px;\n }\n }\n }\n\n &-iconLeft {\n .input-matLabel {\n left: var(--input-icon-gutter);\n\n &-floating {\n left: calc(var(--input-icon-gutter) + 0.5em);\n }\n }\n\n .input-placeholder {\n left: var(--input-icon-gutter);\n }\n\n .input-fieldset {\n padding-left: var(--input-icon-gutter);\n }\n }\n\n &-container {\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n background-color: var(--input-background);\n transition: border-color;\n cursor: text;\n height: 100%;\n\n &-multiline {\n overflow: hidden;\n padding-top: 10px;\n align-items: flex-start;\n }\n\n &-border {\n outline: 1px solid var(--border-color);\n transition: border-color;\n background-color: var(--input-background);\n outline: none;\n\n &-focused {\n outline-color: var(--primary-color);\n border-color: var(--primary-color);\n }\n\n &-searchFocused {\n outline-color: var(--search-input-color);\n outline-width: 2px;\n border-color: var(--search-input-color);\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n }\n\n &-label {\n display: flex;\n color: var(--text-color);\n height: 100%;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n white-space: nowrap;\n margin-right: 5px;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n color: color(var(--border-color) lightness(40%));\n background-color: transparent;\n font-size: 12px;\n position: absolute;\n top: -15px;\n white-space: nowrap;\n }\n }\n\n &-upper-right-label {\n position: absolute;\n transform: translateY(calc(-100% - 1.5px));\n right: 8px;\n min-height: 12px;\n font-size: 12px;\n color: color(var(--border-color) lightness(40%));\n }\n\n &-field {\n appearance: none;\n -webkit-appearance: none;\n font-family: inherit;\n font-size: inherit;\n box-sizing: border-box;\n resize: none;\n width: 100%;\n border: none;\n border-radius: var(--default-border-radius);\n overflow: hidden;\n line-height: 1.25;\n color: var(--text-color);\n background: transparent;\n\n &:focus {\n outline: none;\n }\n\n &::placeholder {\n color: var(--text-light);\n }\n\n /* The browser's own search-cancel decoration doesn't match the theme — Search has its own\n themed clear icon (see isSearchCleared in input.tsx) that replaces it. */\n &::-webkit-search-cancel-button,\n &::-webkit-search-decoration {\n -webkit-appearance: none;\n appearance: none;\n display: none;\n }\n\n &-withIcon {\n padding-right: var(--input-icon-gutter) !important;\n\n &-left {\n padding-right: 0.643em !important;\n padding-left: var(--input-icon-gutter) !important;\n }\n }\n\n &-suffix {\n padding-right: 9px;\n }\n\n &-date {\n padding: 6px 9px;\n color-scheme: dark;\n\n svg {\n color: white;\n }\n }\n\n &-default {\n padding: 8px 7.5px 6px;\n }\n\n &[disabled] {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n opacity: 1;\n\n &::placeholder {\n color: var(--disabled-font-color);\n -webkit-text-fill-color: var(--disabled-font-color);\n }\n }\n\n &-readOnly {\n cursor: default;\n color: var(--text-dark);\n -webkit-text-fill-color: var(--text-dark);\n }\n\n &-uppercase {\n text-transform: uppercase;\n }\n\n &-multiline {\n overflow-y: auto;\n resize: vertical;\n padding-top: 4px;\n min-height: 30px;\n\n &-autoResize {\n resize: none;\n overflow: hidden;\n }\n }\n\n &-icon {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: transparent;\n border: none;\n position: absolute;\n outline: none;\n right: var(--input-icon-offset);\n color: var(--input-icon-color);\n width: var(--input-icon-size) !important;\n height: var(--input-icon-size) !important;\n user-select: none;\n top: 50%;\n transform: translateY(-50%);\n\n &-left {\n left: var(--input-icon-offset);\n }\n\n &-notClickable {\n pointer-events: none;\n }\n }\n }\n\n &-error {\n height: 1em;\n padding-top: 3px;\n color: var(--error-font-color);\n font-size: 11px;\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 0.5;\n }\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=function(e){return e.Edit=`edit`,e.Phone=`phone`,e.Email=`email`,e.Iban=`iban`,e.Date=`date`,e.HasNoIcon=`hasNoIcon`,e.Search=`searchInput`,e.Password=`password`,e}({});export{e as InputType};
|
|
1
|
+
var e=function(e){return e.Edit=`edit`,e.Phone=`phone`,e.Email=`email`,e.Iban=`iban`,e.Date=`date`,e.HasNoIcon=`hasNoIcon`,e.Search=`searchInput`,e.Password=`password`,e}({}),t=function(e){return e.Left=`left`,e.Right=`right`,e}({});export{t as InputIconPosition,e as InputType};
|
|
2
2
|
//# sourceMappingURL=input.types.mjs.map
|
|
@@ -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, HtmlProps } 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 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>,\n HtmlProps {\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?: ((value: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void) | 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 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"}
|
|
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, HtmlProps } 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>,\n HtmlProps {\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?: ((value: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void) | 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,2 +1,2 @@
|
|
|
1
|
-
import e from"../actionButtons/actionButtons.module.mjs";import{ButtonStyleType as t}from"../button/button.types.mjs";import n from"../button/button.mjs";import{Align as r}from"../floatingMenu/floatingMenu.types.mjs";import i from"../floatingMenu/floatingMenu.mjs";import a from"../chip/chip.mjs";import o from"../chip/chipContainer.mjs";import s from"../modal/modal.mjs";import c from"../../services/klipy.mjs";import l from"../searchInput/searchInput.mjs";import u from"./components/gifPreview/gifPreview.mjs";import d from"./components/gifView/gifView.mjs";import f from"./klipyPicker.module.mjs";import{useMemo as p,useState as m}from"react";import h from"classnames";import{Fragment as g,jsx as _,jsxs as v}from"react/jsx-runtime";function y({className:y,token:b=``,isModal:x=!1,onSelect:S=()=>void 0,selected:C=null}){let w=p(()=>new c(b),[b]),[T,E]=m(``),[D,O]=m([]),[k,A]=m([]),[j,M]=m(null),[N,P]=m(!1);async function F(e){if(E(e),!e.trim()){O([]),A([]),M(null);return}let[{results:t=[],next:n=null},{results:r=[]}]=await Promise.all([w.search(e,{limit:18,contentfilter:`medium`}),w.searchSuggestions(e)]);O(t),M(n),A(r)}async function I(){let{results:e=[],next:t=null}=await w.search(T,{limit:36,pos:j??void 0,contentfilter:`medium`});M(t),O(t=>[...t,...e])}async function L(e){await S(e),P(!1),e&&w.registerShare(T,e.id)}function R(){return _(o,{inline:!0,className:f.klipyPickerSuggestionsContainer,children:k.map(e=>_(a,{className:f.klipyPickerSuggestionsItem,text:e,onClick:()=>F(e)},e))})}function z(){return _(u,{previewItems:D,onSelect:L,onLoadAdditional:I})}function B(){return _(l,{label:`Pick gif`,value:T,onDebounce:F,onChange:E})}return x?v(`div`,{className:h(f.klipyPicker,y),children:[v(`div`,{className:e.actionButtons,children:[_(n,{onClick:()=>P(!0),children:`Pick gif`}),C&&_(n,{styleType:t.Delete,onClick:()=>L(null),children:`Remove gif`})]}),C&&_(d,{className:f.klipyPickerSelected,src:C}),_(s,{isOpen:N,onClose:()=>P(!1),children:v(`div`,{className:f.klipyPickerModalContent,children:[B(),R(),z()]})})]}):v(`div`,{className:h(f.klipyPicker,y),children:[_(i,{align:r.Left,isOpen:D.length>0&&N,onOpenChange:P,content:v(g,{children:[R(),z()]}),children:B()}),C&&_(n,{styleType:t.Delete,onClick:()=>L(null),children:`Remove gif`}),C&&_(d,{className:f.klipyPickerSelected,src:C})]})}export{y as default};
|
|
1
|
+
import e from"../actionButtons/actionButtons.module.mjs";import{ButtonStyleType as t}from"../button/button.types.mjs";import n from"../button/button.mjs";import{Align as r}from"../floatingMenu/floatingMenu.types.mjs";import i from"../floatingMenu/floatingMenu.mjs";import a from"../chip/chip.mjs";import o from"../chip/chipContainer.mjs";import s from"../modal/modal.mjs";import c from"../../services/klipy.mjs";import l from"../searchInput/searchInput.mjs";import u from"./components/gifPreview/gifPreview.mjs";import d from"./components/gifView/gifView.mjs";import f from"./klipyPicker.module.mjs";import{useMemo as p,useState as m}from"react";import h from"classnames";import{Fragment as g,jsx as _,jsxs as v}from"react/jsx-runtime";function y({className:y,token:b=``,isModal:x=!1,onSelect:S=()=>void 0,selected:C=null}){let w=p(()=>new c(b),[b]),[T,E]=m(``),[D,O]=m([]),[k,A]=m([]),[j,M]=m(null),[N,P]=m(!1);async function F(e){if(E(e),!e.trim()){O([]),A([]),M(null);return}let[{results:t=[],next:n=null},{results:r=[]}]=await Promise.all([w.search(e,{limit:18,contentfilter:`medium`}),w.searchSuggestions(e)]);O(t),M(n),A(r)}async function I(){let{results:e=[],next:t=null}=await w.search(T,{limit:36,pos:j??void 0,contentfilter:`medium`});M(t),O(t=>[...t,...e])}async function L(e){await S(e),P(!1),e&&w.registerShare(T,e.id)}function R(){return _(o,{inline:!0,className:f.klipyPickerSuggestionsContainer,children:k.map(e=>_(a,{className:f.klipyPickerSuggestionsItem,text:e,onClick:()=>F(e)},e))})}function z(){return _(u,{previewItems:D,onSelect:L,onLoadAdditional:I})}function B(){return _(l,{label:`Pick gif`,value:T,onDebounce:F,onChange:E})}return x?v(`div`,{className:h(f.klipyPicker,y),children:[v(`div`,{className:e.actionButtons,children:[_(n,{onClick:()=>P(!0),children:`Pick gif`}),C&&_(n,{styleType:t.Delete,onClick:()=>L(null),children:`Remove gif`})]}),C&&_(d,{className:f.klipyPickerSelected,src:C}),_(s,{isOpen:N,onClose:()=>P(!1),children:v(`div`,{className:f.klipyPickerModalContent,children:[B(),R(),z()]})})]}):v(`div`,{className:h(f.klipyPicker,y),children:[_(i,{align:r.Left,isOpen:D.length>0&&N,onOpenChange:P,dontCloseOnChildClick:!0,content:v(g,{children:[R(),z()]}),children:B()}),C&&_(n,{styleType:t.Delete,onClick:()=>L(null),children:`Remove gif`}),C&&_(d,{className:f.klipyPickerSelected,src:C})]})}export{y 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 = [] }] =\n 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 (\n <SearchInput\n label=\"Pick gif\"\n value={searchText}\n onDebounce={searchFor}\n onChange={setSearchText}\n />\n );\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 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
|
+
{"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 = [] }] =\n 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 (\n <SearchInput\n label=\"Pick gif\"\n value={searchText}\n onDebounce={searchFor}\n onChange={setSearchText}\n />\n );\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 t from"../interactableDiv/index.mjs";import n from"../input/input.mjs";import r from"./numberInput.module.mjs";import"react";import i from"classnames";import{FontAwesomeIcon as a}from"@fortawesome/react-fontawesome";import{faChevronDown as o,faChevronUp as s}from"@fortawesome/free-solid-svg-icons";import{jsx as c,jsxs as l}from"react/jsx-runtime";import{NumericFormat as u}from"react-number-format";function d(d){let{value:f,onChange:p,formValidator:m,label:h,error:g,className:_,allowNegative:v=!1,decimalSeparator:y,thousandSeparator:b,prefix:x,suffix:S,decimalScale:C,fixedDecimalScale:w=!0,placeholder:T,sign:E,showArrows:D=!1,step:O=1,readOnly:k=!1,onKeyDown:A,...j}=e(`NumberInput`,d),M=m==null?f:m.value,N=E!=null&&M!=null?Math.abs(Number(M)):M,P=N==null?``:String(N);function F(e){let t=e;t!==void 0&&(E===`-`&&(t=-Math.abs(t)),E===`+`&&(t=Math.abs(t))),m?.set(t??null),p?.(t)}function I(e){if(k)return;let t=(M!=null&&M!==``?Number(M):0)+e;!v&&E!==`-`&&(t=Math.max(0,t)),E===`-`&&(t=Math.min(0,t)),F(t)}function L(e){if(k){A?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),I(e.shiftKey?O*10:O);return}if(e.key===`ArrowDown`){e.preventDefault(),I(e.shiftKey?-O*10:-O);return}A?.(e)}let R=E===`-`?`-`:E===`+`?`+`:x,z=D&&!k?l(`div`,{className:r.arrows,children:[c(t,{className:r.arrowBtn,tabIndex:-1,onMouseDown:e=>{e.preventDefault(),I(e.shiftKey?O*10:O)},children:c(a,{icon:s})}),c(t,{className:r.arrowBtn,tabIndex:-1,onMouseDown:e=>{e.preventDefault(),I(e.shiftKey?-O*10:-O)},children:c(a,{icon:o})})]}):null;return c(n,{label:h,className:i(r.numberInput,_),value:P,onChange:()=>{},formValidator:null,isValid:m?m.touched?!m.hasError():!0:!g,errorMessage:m?m.getCurrentErrorMessage()??``:g??``,placeholder:T,readOnly:k,suffix:z,customInput:c(u,{onValueChange:e=>F(e.floatValue),allowNegative:E==null?v:!1,decimalSeparator:y,thousandSeparator:b,decimalScale:C,fixedDecimalScale:w,prefix:R,suffix:S,onKeyDown:D?L:A,...j})})}export{d as default};
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import t from"../interactableDiv/index.mjs";import n from"../input/input.mjs";import r from"./numberInput.module.mjs";import"react";import i from"classnames";import{FontAwesomeIcon as a}from"@fortawesome/react-fontawesome";import{faChevronDown as o,faChevronUp as s}from"@fortawesome/free-solid-svg-icons";import{jsx as c,jsxs as l}from"react/jsx-runtime";import{NumericFormat as u}from"react-number-format";function d(d){let{value:f,onChange:p,formValidator:m,label:h,error:g,className:_,allowNegative:v=!1,decimalSeparator:y,thousandSeparator:b,prefix:x,suffix:S,decimalScale:C,fixedDecimalScale:w=!0,placeholder:T,sign:E,showArrows:D=!1,step:O=1,readOnly:k=!1,onKeyDown:A,...j}=e(`NumberInput`,d),M=m==null?f:m.value,N=E!=null&&M!=null?Math.abs(Number(M)):M,P=N==null?``:String(N);function F(e){let t=e;t!==void 0&&(E===`-`&&(t=-Math.abs(t)),E===`+`&&(t=Math.abs(t))),m?.set(t??null),p?.(t)}function I(e){if(k)return;let t=(M!=null&&M!==``?Number(M):0)+e;!v&&E!==`-`&&(t=Math.max(0,t)),E===`-`&&(t=Math.min(0,t)),F(t)}function L(e){if(k){A?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),I(e.shiftKey?O*10:O);return}if(e.key===`ArrowDown`){e.preventDefault(),I(e.shiftKey?-O*10:-O);return}A?.(e)}let R=E===`-`?`-`:E===`+`?`+`:x,z=D&&!k?l(`div`,{className:r.arrows,children:[c(t,{className:r.arrowBtn,tabIndex:-1,onMouseDown:e=>{e.preventDefault(),I(e.shiftKey?O*10:O)},children:c(a,{icon:s})}),c(t,{className:r.arrowBtn,tabIndex:-1,onMouseDown:e=>{e.preventDefault(),I(e.shiftKey?-O*10:-O)},children:c(a,{icon:o})})]}):null;return c(n,{label:h,className:i(r.numberInput,_),value:P,onChange:()=>{},formValidator:null,isValid:m?m.touched?!m.hasError():!0:!g,errorMessage:m?m.getCurrentErrorMessage()??``:g??``,placeholder:T,readOnly:k,suffix:z,inputMode:C===0?`numeric`:`decimal`,customInput:c(u,{onValueChange:e=>F(e.floatValue),allowNegative:E==null?v:!1,decimalSeparator:y,thousandSeparator:b,decimalScale:C,fixedDecimalScale:w,prefix:R,suffix:S,onKeyDown:D?L:A,...j})})}export{d 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, NumericFormatProps } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults } from '../configProvider';\n\nexport interface NumberInputProps\n extends Omit<NumericFormatProps, 'onValueChange' | 'value' | 'customInput' | 'onChange' | 'step' | 'onKeyDown'> {\n value?: number | string;\n onChange?: (value: number | undefined) => void;\n formValidator?: FormValidator | null;\n label?: string;\n error?: string;\n className?: string;\n allowNegative?: boolean;\n decimalSeparator?: string;\n thousandSeparator?: string | boolean;\n prefix?: string;\n suffix?: string;\n decimalScale?: number;\n /**\n * Whether to pad the value to exactly `decimalScale` decimals. Default `true`\n * (e.g. with `decimalScale={2}`, `12` displays as `12.00`). Set `false` to make\n * `decimalScale` an upper limit only — the user may type fewer decimals and they\n * are kept as-is (`12` stays `12`, `12.1` stays `12.1`), while anything beyond\n * `decimalScale` is still truncated.\n */\n fixedDecimalScale?: boolean;\n placeholder?: string;\n sign?: '+' | '-';\n showArrows?: boolean;\n step?: number;\n readOnly?: boolean;\n onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\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 readOnly = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\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 handleStep(delta: number) {\n if (readOnly) return;\n const current = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n let next = current + delta;\n if (!allowNegative && sign !== '-') next = Math.max(0, next);\n if (sign === '-') next = Math.min(0, next);\n handleValueChange(next);\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n if (readOnly) { onKeyDownProp?.(e); return; }\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 arrowSuffix = showArrows && !readOnly ? (\n <div className={styles.arrows}>\n <InteractableDiv\n className={styles.arrowBtn}\n tabIndex={-1}\n onMouseDown={(e) => { e.preventDefault(); handleStep(e.shiftKey ? step * 10 : step); }}\n >\n <FontAwesomeIcon icon={faChevronUp} />\n </InteractableDiv>\n <InteractableDiv\n className={styles.arrowBtn}\n tabIndex={-1}\n onMouseDown={(e) => { e.preventDefault(); handleStep(e.shiftKey ? -step * 10 : -step); }}\n >\n <FontAwesomeIcon icon={faChevronDown} />\n </InteractableDiv>\n </div>\n ) : null;\n\n return (\n <Input\n label={label}\n className={cx(styles.numberInput, className)}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isValid={formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error}\n errorMessage={formValidator ? (formValidator.getCurrentErrorMessage() ?? '') : (error ?? '')}\n placeholder={placeholder}\n readOnly={readOnly}\n suffix={arrowSuffix}\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={showArrows ? handleKeyDown : onKeyDownProp}\n {...rest}\n />\n }\n />\n );\n}\n\nexport default NumberInput;\n"],"mappings":"0eA2CA,SAAS,EAAY,EAAuC,CAC1D,GAAM,CACN,QACA,WACA,gBACA,QACA,QACA,YACA,gBAAgB,GAChB,mBACA,oBACA,SACA,SACA,eACA,oBAAoB,GACpB,cACA,OACA,aAAa,GACb,OAAO,EACP,WAAW,GACX,UAAW,EACX,GAAG,GACC,EAAqB,cAAe,CAAK,EACvC,EAAW,GAAiB,KAAgD,EAAzC,EAAc,MAEjD,EAAe,GAAQ,MAAQ,GAAY,KAAO,KAAK,IAAI,OAAO,CAAQ,CAAC,EAAI,EAC/E,EAAkB,GAAgB,KAA8B,GAAvB,OAAO,CAAY,EAElE,SAAS,EAAkB,EAAgC,CACzD,IAAI,EAAM,EACN,IAAQ,IAAA,KACN,IAAS,MAAK,EAAM,CAAC,KAAK,IAAI,CAAG,GACjC,IAAS,MAAK,EAAO,KAAK,IAAI,CAAG,IAEnC,GAAqC,IAAI,GAAO,IAAI,EACxD,IAAW,CAAG,CAChB,CAEA,SAAS,EAAW,EAAe,CACjC,GAAI,EAAU,OAEd,IAAI,GADY,GAAY,MAAQ,IAAa,GAAK,OAAO,CAAQ,EAAI,GACpD,EACjB,CAAC,GAAiB,IAAS,MAAK,EAAO,KAAK,IAAI,EAAG,CAAI,GACvD,IAAS,MAAK,EAAO,KAAK,IAAI,EAAG,CAAI,GACzC,EAAkB,CAAI,CACxB,CAEA,SAAS,EAAc,EAA0C,CAC/D,GAAI,EAAU,CAAE,IAAgB,CAAC,EAAG,MAAQ,CAC5C,GAAI,EAAE,MAAQ,UAAW,CACvB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,EAAO,GAAK,CAAI,EACxC,MACF,CACA,GAAI,EAAE,MAAQ,YAAa,CACzB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,CAAC,EAAO,GAAK,CAAC,CAAI,EAC1C,MACF,CACA,IAAgB,CAAC,CACnB,CAEA,IAAM,EAAiB,IAAS,IAAM,IAAM,IAAS,IAAM,IAAM,EAE3D,EAAc,GAAc,CAAC,EACjC,EAAC,MAAD,CAAK,UAAW,EAAO,gBAAvB,CACE,EAAC,EAAD,CACE,UAAW,EAAO,SAClB,SAAU,GACV,YAAc,GAAM,CAAE,EAAE,eAAe,EAAG,EAAW,EAAE,SAAW,EAAO,GAAK,CAAI,CAAG,WAErF,EAAC,EAAD,CAAiB,KAAM,CAAc,CAAA,CACtB,CAAA,EACjB,EAAC,EAAD,CACE,UAAW,EAAO,SAClB,SAAU,GACV,YAAc,GAAM,CAAE,EAAE,eAAe,EAAG,EAAW,EAAE,SAAW,CAAC,EAAO,GAAK,CAAC,CAAI,CAAG,WAEvF,EAAC,EAAD,CAAiB,KAAM,CAAgB,CAAA,CACxB,CAAA,CACd,IACH,KAEJ,OACE,EAAC,EAAD,CACS,QACP,UAAW,EAAG,EAAO,YAAa,CAAS,EAC3C,MAAO,EACP,aAAgB,CAAC,EACjB,cAAe,KACf,QAAS,EAAiB,EAAc,QAAU,CAAC,EAAc,SAAS,EAAI,GAAQ,CAAC,EACvF,aAAc,EAAiB,EAAc,uBAAuB,GAAK,GAAO,GAAS,GAC5E,cACH,WACV,OAAQ,EACR,YACE,EAAC,EAAD,CACE,cAAgB,GAAS,EAAkB,EAAK,UAAU,EAC1D,cAAe,GAAQ,KAAe,EAAR,GACZ,mBACC,oBACL,eACK,oBACnB,OAAQ,EACA,SACR,UAAW,EAAa,EAAgB,EACxC,GAAI,CACL,CAAA,CAEJ,CAAA,CAEL"}
|
|
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, NumericFormatProps } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults } from '../configProvider';\n\nexport interface NumberInputProps\n extends Omit<NumericFormatProps, 'onValueChange' | 'value' | 'customInput' | 'onChange' | 'step' | 'onKeyDown'> {\n value?: number | string;\n onChange?: (value: number | undefined) => void;\n formValidator?: FormValidator | null;\n label?: string;\n error?: string;\n className?: string;\n allowNegative?: boolean;\n decimalSeparator?: string;\n thousandSeparator?: string | boolean;\n prefix?: string;\n suffix?: string;\n decimalScale?: number;\n /**\n * Whether to pad the value to exactly `decimalScale` decimals. Default `true`\n * (e.g. with `decimalScale={2}`, `12` displays as `12.00`). Set `false` to make\n * `decimalScale` an upper limit only — the user may type fewer decimals and they\n * are kept as-is (`12` stays `12`, `12.1` stays `12.1`), while anything beyond\n * `decimalScale` is still truncated.\n */\n fixedDecimalScale?: boolean;\n placeholder?: string;\n sign?: '+' | '-';\n showArrows?: boolean;\n step?: number;\n readOnly?: boolean;\n onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\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 readOnly = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\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 handleStep(delta: number) {\n if (readOnly) return;\n const current = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n let next = current + delta;\n if (!allowNegative && sign !== '-') next = Math.max(0, next);\n if (sign === '-') next = Math.min(0, next);\n handleValueChange(next);\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n if (readOnly) { onKeyDownProp?.(e); return; }\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 arrowSuffix = showArrows && !readOnly ? (\n <div className={styles.arrows}>\n <InteractableDiv\n className={styles.arrowBtn}\n tabIndex={-1}\n onMouseDown={(e) => { e.preventDefault(); handleStep(e.shiftKey ? step * 10 : step); }}\n >\n <FontAwesomeIcon icon={faChevronUp} />\n </InteractableDiv>\n <InteractableDiv\n className={styles.arrowBtn}\n tabIndex={-1}\n onMouseDown={(e) => { e.preventDefault(); handleStep(e.shiftKey ? -step * 10 : -step); }}\n >\n <FontAwesomeIcon icon={faChevronDown} />\n </InteractableDiv>\n </div>\n ) : null;\n\n return (\n <Input\n label={label}\n className={cx(styles.numberInput, className)}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isValid={formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error}\n errorMessage={formValidator ? (formValidator.getCurrentErrorMessage() ?? '') : (error ?? '')}\n placeholder={placeholder}\n readOnly={readOnly}\n suffix={arrowSuffix}\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={showArrows ? handleKeyDown : onKeyDownProp}\n {...rest}\n />\n }\n />\n );\n}\n\nexport default NumberInput;\n"],"mappings":"0eA2CA,SAAS,EAAY,EAAuC,CAC1D,GAAM,CACN,QACA,WACA,gBACA,QACA,QACA,YACA,gBAAgB,GAChB,mBACA,oBACA,SACA,SACA,eACA,oBAAoB,GACpB,cACA,OACA,aAAa,GACb,OAAO,EACP,WAAW,GACX,UAAW,EACX,GAAG,GACC,EAAqB,cAAe,CAAK,EACvC,EAAW,GAAiB,KAAgD,EAAzC,EAAc,MAEjD,EAAe,GAAQ,MAAQ,GAAY,KAAO,KAAK,IAAI,OAAO,CAAQ,CAAC,EAAI,EAC/E,EAAkB,GAAgB,KAA8B,GAAvB,OAAO,CAAY,EAElE,SAAS,EAAkB,EAAgC,CACzD,IAAI,EAAM,EACN,IAAQ,IAAA,KACN,IAAS,MAAK,EAAM,CAAC,KAAK,IAAI,CAAG,GACjC,IAAS,MAAK,EAAO,KAAK,IAAI,CAAG,IAEnC,GAAqC,IAAI,GAAO,IAAI,EACxD,IAAW,CAAG,CAChB,CAEA,SAAS,EAAW,EAAe,CACjC,GAAI,EAAU,OAEd,IAAI,GADY,GAAY,MAAQ,IAAa,GAAK,OAAO,CAAQ,EAAI,GACpD,EACjB,CAAC,GAAiB,IAAS,MAAK,EAAO,KAAK,IAAI,EAAG,CAAI,GACvD,IAAS,MAAK,EAAO,KAAK,IAAI,EAAG,CAAI,GACzC,EAAkB,CAAI,CACxB,CAEA,SAAS,EAAc,EAA0C,CAC/D,GAAI,EAAU,CAAE,IAAgB,CAAC,EAAG,MAAQ,CAC5C,GAAI,EAAE,MAAQ,UAAW,CACvB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,EAAO,GAAK,CAAI,EACxC,MACF,CACA,GAAI,EAAE,MAAQ,YAAa,CACzB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,CAAC,EAAO,GAAK,CAAC,CAAI,EAC1C,MACF,CACA,IAAgB,CAAC,CACnB,CAEA,IAAM,EAAiB,IAAS,IAAM,IAAM,IAAS,IAAM,IAAM,EAE3D,EAAc,GAAc,CAAC,EACjC,EAAC,MAAD,CAAK,UAAW,EAAO,gBAAvB,CACE,EAAC,EAAD,CACE,UAAW,EAAO,SAClB,SAAU,GACV,YAAc,GAAM,CAAE,EAAE,eAAe,EAAG,EAAW,EAAE,SAAW,EAAO,GAAK,CAAI,CAAG,WAErF,EAAC,EAAD,CAAiB,KAAM,CAAc,CAAA,CACtB,CAAA,EACjB,EAAC,EAAD,CACE,UAAW,EAAO,SAClB,SAAU,GACV,YAAc,GAAM,CAAE,EAAE,eAAe,EAAG,EAAW,EAAE,SAAW,CAAC,EAAO,GAAK,CAAC,CAAI,CAAG,WAEvF,EAAC,EAAD,CAAiB,KAAM,CAAgB,CAAA,CACxB,CAAA,CACd,IACH,KAEJ,OACE,EAAC,EAAD,CACS,QACP,UAAW,EAAG,EAAO,YAAa,CAAS,EAC3C,MAAO,EACP,aAAgB,CAAC,EACjB,cAAe,KACf,QAAS,EAAiB,EAAc,QAAU,CAAC,EAAc,SAAS,EAAI,GAAQ,CAAC,EACvF,aAAc,EAAiB,EAAc,uBAAuB,GAAK,GAAO,GAAS,GAC5E,cACH,WACV,OAAQ,EACR,UAAW,IAAiB,EAAI,UAAY,UAC5C,YACE,EAAC,EAAD,CACE,cAAgB,GAAS,EAAkB,EAAK,UAAU,EAC1D,cAAe,GAAQ,KAAe,EAAR,GACZ,mBACC,oBACL,eACK,oBACnB,OAAQ,EACA,SACR,UAAW,EAAa,EAAgB,EACxC,GAAI,CACL,CAAA,CAEJ,CAAA,CAEL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"../bodyEnd/bodyEnd.mjs";import t from"./popover.module.mjs";import{PopoverAlign as
|
|
1
|
+
import e from"../bodyEnd/bodyEnd.mjs";import{tabbablesIn as t,useFocusBoundary as n}from"../hooks/useFocusBoundary.mjs";import r from"./popover.module.mjs";import{PopoverAlign as i,PopoverPlacement as a,PopoverTrigger as o}from"./popover.types.mjs";import{usePopoverPosition as s}from"./usePopoverPosition.mjs";import{useCallback as c,useEffect as l,useRef as u,useState as d}from"react";import f from"classnames";import{jsx as p,jsxs as m}from"react/jsx-runtime";function h({children:h=null,content:g=null,placement:_=a.Bottom,align:v=i.Center,trigger:y=o.Click,isOpen:b,defaultOpen:x=!1,onOpenChange:S=()=>{},offset:C=10,withArrow:w=!0,closeOnOutsideClick:T=!0,closeOnEscape:E=!0,openDelay:D=100,closeDelay:O=120,disabled:k=!1,className:A,style:j,classNames:M,styles:N,...P}){let F=u(null),I=u(null),L=u(null),R=u(null),z=u(void 0),B=b!==void 0,[V,H]=d(x),U=B?b:V,W=u(S);l(()=>{W.current=S});let G=c(e=>{k&&e||(B||H(e),W.current(e))},[k,B]);s({isOpen:!!U,placement:_,align:v,offset:C,triggerRef:F,floatingRef:I,panelRef:L,arrowRef:w?R:{current:null}});let{restoreFocusToTrigger:K,focusAfterTrigger:q}=n({triggerRef:F,contentRef:I});l(()=>{if(!U||y!==o.Click)return;let e=t(L.current);e.length!==0&&(I.current?.setAttribute(`role`,`dialog`),e[0].focus())},[U,y]);function J(e){if(e.key!==`Tab`||y!==o.Click)return;let n=t(L.current);if(n.length===0)return;let r=document.activeElement;!e.shiftKey&&r===n[n.length-1]?(e.preventDefault(),q(),G(!1)):e.shiftKey&&r===n[0]&&(e.preventDefault(),K(),G(!1))}l(()=>{if(!U)return;function e(e){let t=e.target,n=I.current?.contains(t),r=F.current?.contains(t);!n&&!r&&G(!1)}function n(e){e.key===`Escape`&&(I.current?.contains(document.activeElement)&&(t(F.current)[0]??F.current)?.focus(),G(!1))}function r(e){let t=e.target,n=I.current?.contains(t),r=F.current?.contains(t);!n&&!r&&G(!1)}return T&&y!==o.Hover&&(window.addEventListener(`mouseup`,e),document.addEventListener(`focusin`,r)),E&&window.addEventListener(`keydown`,n),()=>{window.removeEventListener(`mouseup`,e),document.removeEventListener(`focusin`,r),window.removeEventListener(`keydown`,n)}},[U,T,E,y,G]),l(()=>()=>clearTimeout(z.current),[]);function Y(e){clearTimeout(z.current),z.current=setTimeout(()=>G(e),e?D:O)}let X=y===o.Click?{onClick:()=>G(!U)}:y===o.Hover?{onMouseEnter:()=>Y(!0),onMouseLeave:()=>Y(!1)}:{onFocus:()=>G(!0),onBlur:()=>G(!1)},Z=y===o.Hover?{onMouseEnter:()=>Y(!0),onMouseLeave:()=>Y(!1)}:{};return m(`div`,{...P,className:f(r.popover,A,M?.root),style:{...j,...N?.root},children:[p(`div`,{ref:F,className:f(r.popoverTrigger,M?.trigger),style:N?.trigger,...X,children:h}),U&&p(e,{children:m(`div`,{ref:I,className:f(r.popoverFloating,M?.floating),style:N?.floating,"data-placement":_,onKeyDown:J,...Z,children:[p(`div`,{ref:L,className:f(r.popoverPanel,M?.panel),style:N?.panel,children:g}),w&&p(`div`,{ref:R,className:f(r.popoverArrow,M?.arrow),style:N?.arrow})]})})]})}export{h as default};
|
|
2
2
|
//# sourceMappingURL=popover.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(',');\n\n/** Tabbable descendants of `root`, in DOM order. */\nfunction tabbablesIn(root: ParentNode | null): HTMLElement[] {\n if (!root) return [];\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n}\n\nfunction Popover({\n children = null,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: PopoverProps) {\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => { onOpenChangeRef.current = onOpenChange; });\n\n const setOpen = useCallback((next: boolean) => {\n if (disabled && next) return;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next);\n }, [disabled, isControlled]);\n\n usePopoverPosition({\n isOpen: !!open,\n placement,\n align,\n offset,\n triggerRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n });\n\n function firstTriggerFocusable(): HTMLElement | null {\n return tabbablesIn(triggerRef.current)[0] ?? triggerRef.current;\n }\n\n // Hand focus back to the trigger — used on Escape and on Shift+Tab out of the panel top.\n function restoreFocusToTrigger() {\n firstTriggerFocusable()?.focus();\n }\n\n // Move focus to the tabbable element that follows the trigger in the document, skipping the\n // portaled panel — the \"natural next\" stop when Tab exits the bottom of the panel.\n function focusAfterTrigger() {\n const all = tabbablesIn(document.body).filter((el) => !floatingRef.current?.contains(el));\n const trig = firstTriggerFocusable();\n const idx = trig ? all.indexOf(trig) : -1;\n if (idx >= 0) all[idx + 1]?.focus();\n }\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }, [open, trigger]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false);\n }\n }\n\n // Outside click + Escape. Registered only while open and only for the relevant trigger mode.\n useEffect(() => {\n if (!open) return;\n\n function onMouseUp(event: MouseEvent) {\n const target = event.target as Node;\n const inPanel = floatingRef.current?.contains(target);\n const inTrigger = triggerRef.current?.contains(target);\n if (!inPanel && !inTrigger) setOpen(false);\n }\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== 'Escape') return;\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) {\n (tabbablesIn(triggerRef.current)[0] ?? triggerRef.current)?.focus();\n }\n setOpen(false);\n }\n // Keyboard counterpart to the outside click: once focus leaves both the trigger\n // and the (portaled) panel — e.g. tabbing on to the next element — close.\n function onFocusIn(event: FocusEvent) {\n const target = event.target as Node;\n const inPanel = floatingRef.current?.contains(target);\n const inTrigger = triggerRef.current?.contains(target);\n if (!inPanel && !inTrigger) setOpen(false);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n if (closeOnEscape) window.addEventListener('keydown', onKeyDown);\n return () => {\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n window.removeEventListener('keydown', onKeyDown);\n };\n }, [open, closeOnOutsideClick, closeOnEscape, trigger, setOpen]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? { onClick: () => setOpen(!open) }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n <div\n ref={floatingRef}\n className={cx(styles.popoverFloating, classNames?.floating)}\n style={slotStyles?.floating}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(styles.popoverPanel, classNames?.panel)}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && (\n <div\n ref={arrowRef}\n className={cx(styles.popoverArrow, classNames?.arrow)}\n style={slotStyles?.arrow}\n />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\nfunction Popover({\n children = null,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: PopoverProps) {\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => { onOpenChangeRef.current = onOpenChange; });\n\n const setOpen = useCallback((next: boolean) => {\n if (disabled && next) return;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next);\n }, [disabled, isControlled]);\n\n usePopoverPosition({\n isOpen: !!open,\n placement,\n align,\n offset,\n triggerRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n });\n\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n triggerRef,\n contentRef: floatingRef,\n });\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }, [open, trigger]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false);\n }\n }\n\n // Outside click + Escape. Registered only while open and only for the relevant trigger mode.\n useEffect(() => {\n if (!open) return;\n\n function onMouseUp(event: MouseEvent) {\n const target = event.target as Node;\n const inPanel = floatingRef.current?.contains(target);\n const inTrigger = triggerRef.current?.contains(target);\n if (!inPanel && !inTrigger) setOpen(false);\n }\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== 'Escape') return;\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) {\n (tabbablesIn(triggerRef.current)[0] ?? triggerRef.current)?.focus();\n }\n setOpen(false);\n }\n // Keyboard counterpart to the outside click: once focus leaves both the trigger\n // and the (portaled) panel — e.g. tabbing on to the next element — close.\n function onFocusIn(event: FocusEvent) {\n const target = event.target as Node;\n const inPanel = floatingRef.current?.contains(target);\n const inTrigger = triggerRef.current?.contains(target);\n if (!inPanel && !inTrigger) setOpen(false);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n if (closeOnEscape) window.addEventListener('keydown', onKeyDown);\n return () => {\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n window.removeEventListener('keydown', onKeyDown);\n };\n }, [open, closeOnOutsideClick, closeOnEscape, trigger, setOpen]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? { onClick: () => setOpen(!open) }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n <div\n ref={floatingRef}\n className={cx(styles.popoverFloating, classNames?.floating)}\n style={slotStyles?.floating}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(styles.popoverPanel, classNames?.panel)}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && (\n <div\n ref={arrowRef}\n className={cx(styles.popoverArrow, classNames?.arrow)}\n style={slotStyles?.arrow}\n />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"gdASA,SAAS,EAAQ,CACf,WAAW,KACX,UAAU,KACV,YAAY,EAAiB,OAC7B,QAAQ,EAAa,OACrB,UAAU,EAAe,MACzB,SACA,cAAc,GACd,mBAAqB,CAAC,EACtB,SAAS,GACT,YAAY,GACZ,sBAAsB,GACtB,gBAAgB,GAChB,YAAY,IACZ,aAAa,IACb,WAAW,GACX,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACY,CACf,IAAM,EAAa,EAAuB,IAAI,EACxC,EAAc,EAAuB,IAAI,EACzC,EAAW,EAAuB,IAAI,EACtC,EAAW,EAAuB,IAAI,EACtC,EAAa,EAAkD,IAAA,EAAS,EAExE,EAAe,IAAW,IAAA,GAC1B,CAAC,EAAc,GAAmB,EAAS,CAAW,EACtD,EAAO,EAAe,EAAS,EAG/B,EAAkB,EAAO,CAAY,EAC3C,MAAgB,CAAE,EAAgB,QAAU,CAAc,CAAC,EAE3D,IAAM,EAAU,EAAa,GAAkB,CACzC,GAAY,IACX,GAAc,EAAgB,CAAI,EACvC,EAAgB,QAAQ,CAAI,EAC9B,EAAG,CAAC,EAAU,CAAY,CAAC,EAE3B,EAAmB,CACjB,OAAQ,CAAC,CAAC,EACV,YACA,QACA,SACA,aACA,cACA,WACA,SAAU,EAAY,EAAW,CAAE,QAAS,IAAK,CACnD,CAAC,EAED,GAAM,CAAE,wBAAuB,qBAAsB,EAAiB,CACpE,aACA,WAAY,CACd,CAAC,EAKD,MAAgB,CACd,GAAI,CAAC,GAAQ,IAAY,EAAe,MAAO,OAC/C,IAAM,EAAa,EAAY,EAAS,OAAO,EAC3C,EAAW,SAAW,IAE1B,EAAY,SAAS,aAAa,OAAQ,QAAQ,EAClD,EAAW,GAAG,MAAM,EACtB,EAAG,CAAC,EAAM,CAAO,CAAC,EAElB,SAAS,EAAsB,EAA4B,CACzD,GAAI,EAAM,MAAQ,OAAS,IAAY,EAAe,MAAO,OAC7D,IAAM,EAAa,EAAY,EAAS,OAAO,EAC/C,GAAI,EAAW,SAAW,EAAG,OAC7B,IAAM,EAAS,SAAS,cACpB,CAAC,EAAM,UAAY,IAAW,EAAW,EAAW,OAAS,IAC/D,EAAM,eAAe,EACrB,EAAkB,EAClB,EAAQ,EAAK,GACJ,EAAM,UAAY,IAAW,EAAW,KACjD,EAAM,eAAe,EACrB,EAAsB,EACtB,EAAQ,EAAK,EAEjB,CAGA,MAAgB,CACd,GAAI,CAAC,EAAM,OAEX,SAAS,EAAU,EAAmB,CACpC,IAAM,EAAS,EAAM,OACf,EAAU,EAAY,SAAS,SAAS,CAAM,EAC9C,EAAY,EAAW,SAAS,SAAS,CAAM,EACjD,CAAC,GAAW,CAAC,GAAW,EAAQ,EAAK,CAC3C,CACA,SAAS,EAAU,EAAsB,CACnC,EAAM,MAAQ,WAEd,EAAY,SAAS,SAAS,SAAS,aAAa,IACrD,EAAY,EAAW,OAAO,EAAE,IAAM,EAAW,UAAU,MAAM,EAEpE,EAAQ,EAAK,EACf,CAGA,SAAS,EAAU,EAAmB,CACpC,IAAM,EAAS,EAAM,OACf,EAAU,EAAY,SAAS,SAAS,CAAM,EAC9C,EAAY,EAAW,SAAS,SAAS,CAAM,EACjD,CAAC,GAAW,CAAC,GAAW,EAAQ,EAAK,CAC3C,CAOA,OALI,GAAuB,IAAY,EAAe,QACpD,OAAO,iBAAiB,UAAW,CAAS,EAC5C,SAAS,iBAAiB,UAAW,CAAS,GAE5C,GAAe,OAAO,iBAAiB,UAAW,CAAS,MAClD,CACX,OAAO,oBAAoB,UAAW,CAAS,EAC/C,SAAS,oBAAoB,UAAW,CAAS,EACjD,OAAO,oBAAoB,UAAW,CAAS,CACjD,CACF,EAAG,CAAC,EAAM,EAAqB,EAAe,EAAS,CAAO,CAAC,EAE/D,UAAsB,aAAa,EAAW,OAAO,EAAG,CAAC,CAAC,EAE1D,SAAS,EAAc,EAAe,CACpC,aAAa,EAAW,OAAO,EAC/B,EAAW,QAAU,eAAiB,EAAQ,CAAI,EAAG,EAAO,EAAY,CAAU,CACpF,CAEA,IAAM,EACJ,IAAY,EAAe,MACvB,CAAE,YAAe,EAAQ,CAAC,CAAI,CAAE,EAChC,IAAY,EAAe,MACzB,CAAE,iBAAoB,EAAc,EAAI,EAAG,iBAAoB,EAAc,EAAK,CAAE,EACpF,CAAE,YAAe,EAAQ,EAAI,EAAG,WAAc,EAAQ,EAAK,CAAE,EAE/D,EACJ,IAAY,EAAe,MACvB,CAAE,iBAAoB,EAAc,EAAI,EAAG,iBAAoB,EAAc,EAAK,CAAE,EACpF,CAAC,EAEP,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,QAAS,EAAW,GAAY,IAAI,EACzD,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,WAHzC,CAKE,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAO,eAAgB,GAAY,OAAO,EACxD,MAAO,GAAY,QACnB,GAAI,EAEH,UACE,CAAA,EACJ,GACC,EAAC,EAAD,CAAA,SACE,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAO,gBAAiB,GAAY,QAAQ,EAC1D,MAAO,GAAY,SACnB,iBAAgB,EAChB,UAAW,EACX,GAAI,WANN,CAQE,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAO,aAAc,GAAY,KAAK,EACpD,MAAO,GAAY,eAElB,CACE,CAAA,EACJ,GACC,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAG,EAAO,aAAc,GAAY,KAAK,EACpD,MAAO,GAAY,KACpB,CAAA,CAEA,GACE,CAAA,CAER,GAET"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{ButtonSize as t,ButtonStyleType as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../interactableDiv/index.mjs";import{Align as a}from"../floatingMenu/floatingMenu.types.mjs";import o from"../floatingMenu/floatingMenu.mjs";import s from"../buttonGroup/buttonGroup.mjs";import c from"./splitButton.module.mjs";import{useEffect as l,useRef as u,useState as d}from"react";import f from"classnames";import{FontAwesomeIcon as p}from"@fortawesome/react-fontawesome";import{faChevronDown as m}from"@fortawesome/free-solid-svg-icons";import{jsx as h,jsxs as g}from"react/jsx-runtime";function _(e,t){return(e%t+t)%t}function v(v){let{children:y,onClick:b=()=>{},actions:x=[],styleType:S=n.Primary,size:C=t.Default,disabled:w=!1,isLoading:T=!1,align:E=a.Right,className:D,style:O,classNames:k,styles:A,...j}=e(`SplitButton`,v),[M,N]=d(!1),P=u([]),[F,I]=d(w);w!==F&&(I(w),w&&N(!1)),l(()=>{if(!M)return;let e=x.findIndex(e=>!e.disabled);e!==-1&&P.current[e]?.focus()},[M]);function L(e,t){let n=x.map((e,t)=>t).filter(e=>!x[e].disabled);if(n.length===0)return;let r=n.indexOf(e),i=r===-1?t===1?0:n.length-1:_(r+t,n.length);P.current[n[i]]?.focus()}function R(e){let t=P.current.findIndex(e=>e===document.activeElement);e.key===`ArrowDown`?(e.preventDefault(),L(t,1)):e.key===`ArrowUp`?(e.preventDefault(),L(t,-1)):e.key===`Home`?(e.preventDefault(),L(-1,1)):e.key===`End`&&(e.preventDefault(),L(-1,-1))}return g(s,{...j,className:f(c.splitButton,k?.root,D),style:{...O,...A?.root},children:[h(r,{styleType:S,size:C,disabled:w,isLoading:T,onClick:b,className:f(c.splitButtonPrimary,k?.primaryButton),style:A?.primaryButton,children:y}),h(o,{isOpen:M,onOpenChange:N,align:E,dontCloseOnChildClick:!0,content:h(`ul`,{className:f(c.splitButtonMenu,k?.menu),style:A?.menu,role:`menu`,onKeyDown:R,children:x.map((e,t)=>h(`li`,{role:`none`,children:g(i,{ref:e=>{P.current[t]=e},role:`menuitem`,tabIndex:e.disabled?-1:0,"aria-disabled":e.disabled,className:f(c.splitButtonMenuItem,{[c.splitButtonMenuItemDisabled]:e.disabled},k?.menuItem),style:A?.menuItem,onClick:()=>{w||e.disabled||(e.onClick(),N(!1))},children:[e.icon?h(p,{icon:e.icon,className:c.splitButtonMenuItemIcon}):null,e.label]})},e.id))}),children:h(r,{styleType:S,size:C,disabled:w,icon:m,"aria-label":`More actions`,className:f(c.splitButtonTrigger,k?.triggerButton),style:A?.triggerButton})})]})}export{v as default};
|
|
2
|
+
//# sourceMappingURL=splitButton.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitButton.mjs","names":[],"sources":["../../../../package/common/splitButton/splitButton.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { KeyboardEvent } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Button from '../button';\nimport { ButtonStyleType, ButtonSize } from '../button/button.types';\nimport ButtonGroup from '../buttonGroup';\nimport FloatingMenu from '../floatingMenu';\nimport { Align } from '../floatingMenu/floatingMenu.types';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults } from '../configProvider';\nimport type { SplitButtonProps } from './splitButton.types';\n\nimport styles from './splitButton.module.pcss';\n\nfunction mod(n: number, m: number) {\n return ((n % m) + m) % m;\n}\n\nfunction SplitButton(props: SplitButtonProps) {\n const {\n children,\n onClick = () => {},\n actions = [],\n styleType = ButtonStyleType.Primary,\n size = ButtonSize.Default,\n disabled = false,\n isLoading = false,\n align = Align.Right,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('SplitButton', props);\n\n const [isOpen, setIsOpen] = useState(false);\n const itemRefs = useRef<(HTMLDivElement | null)[]>([]);\n\n // Adjusting state during render (not in an effect) on a prop change is the documented React\n // pattern for this: closing the menu as soon as `disabled` becomes true, without an extra\n // render tick or a setState-in-effect lint violation.\n const [prevDisabled, setPrevDisabled] = useState(disabled);\n if (disabled !== prevDisabled) {\n setPrevDisabled(disabled);\n if (disabled) setIsOpen(false);\n }\n\n useEffect(() => {\n if (!isOpen) return;\n const firstEnabled = actions.findIndex((action) => !action.disabled);\n if (firstEnabled !== -1) itemRefs.current[firstEnabled]?.focus();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n function focusEnabledItem(fromIndex: number, direction: 1 | -1) {\n const enabledIndexes = actions\n .map((action, index) => index)\n .filter((index) => !actions[index].disabled);\n if (enabledIndexes.length === 0) return;\n\n const currentPos = enabledIndexes.indexOf(fromIndex);\n const nextPos = currentPos === -1\n ? (direction === 1 ? 0 : enabledIndexes.length - 1)\n : mod(currentPos + direction, enabledIndexes.length);\n itemRefs.current[enabledIndexes[nextPos]]?.focus();\n }\n\n function handleMenuKeyDown(event: KeyboardEvent<HTMLUListElement>) {\n const activeIndex = itemRefs.current.findIndex((el) => el === document.activeElement);\n\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n focusEnabledItem(activeIndex, 1);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n focusEnabledItem(activeIndex, -1);\n } else if (event.key === 'Home') {\n event.preventDefault();\n focusEnabledItem(-1, 1);\n } else if (event.key === 'End') {\n event.preventDefault();\n focusEnabledItem(-1, -1);\n }\n // Escape and Tab-boundary handling live in FloatingMenu itself, since any menu content with\n // focusable elements needs the same treatment, not just this one.\n }\n\n return (\n <ButtonGroup\n {...rest}\n className={cx(styles.splitButton, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <Button\n styleType={styleType}\n size={size}\n disabled={disabled}\n isLoading={isLoading}\n onClick={onClick}\n className={cx(styles.splitButtonPrimary, classNames?.primaryButton)}\n style={slotStyles?.primaryButton}\n >\n {children}\n </Button>\n\n <FloatingMenu\n isOpen={isOpen}\n onOpenChange={setIsOpen}\n align={align}\n dontCloseOnChildClick\n content={\n <ul\n className={cx(styles.splitButtonMenu, classNames?.menu)}\n style={slotStyles?.menu}\n role=\"menu\"\n onKeyDown={handleMenuKeyDown}\n >\n {actions.map((action, index) => (\n <li key={action.id} role=\"none\">\n <InteractableDiv\n ref={(el) => { itemRefs.current[index] = el; }}\n role=\"menuitem\"\n tabIndex={action.disabled ? -1 : 0}\n aria-disabled={action.disabled}\n className={cx(\n styles.splitButtonMenuItem,\n { [styles.splitButtonMenuItemDisabled]: action.disabled },\n classNames?.menuItem\n )}\n style={slotStyles?.menuItem}\n onClick={() => {\n if (disabled || action.disabled) return;\n action.onClick();\n setIsOpen(false);\n }}\n >\n {action.icon ? <FontAwesomeIcon icon={action.icon} className={styles.splitButtonMenuItemIcon} /> : null}\n {action.label}\n </InteractableDiv>\n </li>\n ))}\n </ul>\n }\n >\n <Button\n styleType={styleType}\n size={size}\n disabled={disabled}\n icon={faChevronDown}\n aria-label=\"More actions\"\n className={cx(styles.splitButtonTrigger, classNames?.triggerButton)}\n style={slotStyles?.triggerButton}\n />\n </FloatingMenu>\n </ButtonGroup>\n );\n}\n\nexport default SplitButton;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={splitButton:`splitButton_5ak5W`,splitButtonPrimary:`splitButtonPrimary_4mVy8`,splitButtonTrigger:`splitButtonTrigger_32cQA`,splitButtonMenu:`splitButtonMenu_P6Ofl`,splitButtonMenuItem:`splitButtonMenuItem_dbEEz`,"splitButtonMenuItem-disabled":`splitButtonMenuItem-disabled_Dw1mr`,splitButtonMenuItemDisabled:`splitButtonMenuItem-disabled_Dw1mr`,"splitButtonMenuItem-icon":`splitButtonMenuItem-icon_dMuKX`,splitButtonMenuItemIcon:`splitButtonMenuItem-icon_dMuKX`};export{e as default};
|
|
2
|
+
//# sourceMappingURL=splitButton.module.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitButton.module.mjs","names":[],"sources":["../../../../package/common/splitButton/splitButton.module.pcss"],"sourcesContent":[".splitButton {\n display: inline-flex;\n}\n\n/* The class is doubled to boost specificity above Button's own .button rule (both are plain\n single classes, so specificity would otherwise tie and the outcome would depend on which\n CSS module happens to load first in the bundle). This guarantees the flattened corner always\n wins regardless of load order. */\n.splitButtonPrimary.splitButtonPrimary {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.splitButtonTrigger.splitButtonTrigger {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n margin-left: -1px;\n}\n\n.splitButtonMenu {\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin: 0;\n padding: 6px;\n list-style: none;\n min-width: 140px;\n}\n\n.splitButtonMenuItem {\n display: flex;\n align-items: center;\n padding: 8px 10px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n &-disabled {\n opacity: 0.5;\n cursor: default;\n pointer-events: none;\n }\n\n &-icon {\n width: 1em;\n flex: none;\n margin-right: 8px;\n }\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"./virtualList.module.mjs";import{lastDescendantIndex as t,resolveReorderTarget as n,resolveTreeDropTarget as r}from"./virtualList.utils.mjs";import{useCallback as i,useEffect as a,useLayoutEffect as o,useRef as s,useState as c}from"react";function l({reorderable:l,treeReorder:u,onReorder:d,onTreeDrop:f,longPressDelay:
|
|
1
|
+
import e from"./virtualList.module.mjs";import{lastDescendantIndex as t,resolveReorderTarget as n,resolveTreeDropTarget as r}from"./virtualList.utils.mjs";import{useCallback as i,useEffect as a,useLayoutEffect as o,useRef as s,useState as c}from"react";function l({reorderable:l,treeReorder:u,onReorder:d,onTreeDrop:f,longPressDelay:ee,dragHandle:p,treeIndentPx:m,keys:h,depths:g,isGroupAt:_,rootRef:v,keysRef:y}){let b=s(null),[x,S]=c(null),[C,w]=c(null),T=s(null),E=s(d),D=s(f),O=s(u),k=s(g),A=s(_),j=s(!0),M=s(null),[N,te]=c(null),P=s(null),F=s(!1),I=s(null),L=s(0),R=s(0),z=s(null),B=s(!1),V=s(null),H=s(0),U=s(0),W=s(0),[G]=c(()=>typeof window<`u`&&typeof window.matchMedia==`function`?window.matchMedia(`(pointer: coarse)`).matches:!1);o(()=>{E.current=d,D.current=f,O.current=u,k.current=g,T.current=C,A.current=_});function K(e){G||M.current!==e&&(M.current=e,te(e))}function ne(e,t){t.buttons===0&&K(t.target.closest(`[data-drag-handle]`)?e:null)}function re(e){e.buttons===0&&K(null)}function ie(e,t){if(t.button!==0)return;let n=!!t.target.closest(`[data-drag-handle]`);j.current=n,n&&K(e)}function ae(e,t){if(p&&!j.current){t.preventDefault();return}b.current=e,S(e),u&&w(null)}function oe(e,n,i){let a=b.current;if(a==null)return null;let o=h.indexOf(a);if(o!==-1&&e>=o&&e<=t(o,g))return null;let s=r(n,i.top,i.height,_(e));return{key:h[e],position:s}}function se(e,t){t.preventDefault();let n=t.currentTarget.getBoundingClientRect(),r=oe(e,t.clientY,n);w(e=>e?.key===r?.key&&e?.position===r?.position?e:r)}function ce(){let e=b.current,t=T.current;e!=null&&t&&t.key!==e&&f?.(e,t)}function le(e){e.preventDefault(),ce(),w(null),b.current=null,S(null),K(null)}function ue(){w(null),b.current=null,S(null),K(null)}function de(e,t){t.preventDefault();let r=b.current;if(r==null)return;let i=h.indexOf(r),a=t.currentTarget.getBoundingClientRect(),o=n(i,e,t.clientY,a.top,a.height);o!=null&&d?.(i,o)}function fe(){b.current=null,S(null),K(null)}let q=i(e=>{let i=b.current,a=v.current;if(i==null||!a)return;let o=V.current?R.current-H.current+W.current/2:R.current,s=document.elementFromPoint(e,o),c=s&&a.contains(s)?s.closest(`[data-index]`):null;if(!c)return;let l=Number(c.dataset.index);if(Number.isNaN(l))return;let u=y.current.indexOf(i),d=c.getBoundingClientRect();if(O.current){let e=y.current,n=k.current;if(u!==-1&&l>=u&&l<=t(u,n)){w(null);return}let i=r(o,d.top,d.height,A.current(l)),a={key:e[l],position:i};w(e=>e?.key===a.key&&e?.position===a.position?e:a);return}let f=n(u,l,o,d.top,d.height);f!=null&&E.current?.(u,f)},[v,y]),J=i(()=>{let e=V.current;e&&(e.style.transform=`translate(${U.current}px, ${R.current-H.current}px)`)},[]),Y=i(()=>{let e=v.current;if(!e||!F.current){z.current=null;return}let t=e.getBoundingClientRect(),n=L.current,r=R.current,i=0;n>=t.left&&n<=t.right&&r>=t.top&&r<=t.bottom&&(r<t.top+48?i=-Math.min(18,Math.ceil((t.top+48-r)/3)):r>t.bottom-48&&(i=Math.min(18,Math.ceil((r-(t.bottom-48))/3)))),i!==0&&(e.scrollTop+=i,q(n)),z.current=requestAnimationFrame(Y)},[q,v]),X=i(()=>{if(P.current!=null&&(clearTimeout(P.current),P.current=null),window.removeEventListener(`touchmove`,Z),window.removeEventListener(`touchend`,X),window.removeEventListener(`touchcancel`,X),z.current!=null&&(cancelAnimationFrame(z.current),z.current=null),V.current&&=(V.current.remove(),null),document.body.style.touchAction=``,document.body.style.overscrollBehavior=``,F.current&&(B.current=!0,setTimeout(()=>{B.current=!1},350),O.current)){let e=b.current,t=T.current;e!=null&&t&&t.key!==e&&D.current?.(e,t)}w(null),F.current=!1,I.current=null,b.current=null,S(null)},[]),Z=i(e=>{let t=e.touches[0];if(t){if(L.current=t.clientX,R.current=t.clientY,!F.current){let e=I.current;e!=null&&Math.abs(t.clientY-e)>10&&P.current!=null&&(clearTimeout(P.current),P.current=null);return}e.cancelable&&e.preventDefault(),J(),q(t.clientX)}},[q,J]);function pe(e,t){if(!l&&!u||p&&!t.target.closest(`[data-drag-handle]`)||P.current!=null||F.current)return;let n=t.touches[0];n&&(I.current=n.clientY,L.current=n.clientX,R.current=n.clientY,window.addEventListener(`touchmove`,Z,{passive:!1}),window.addEventListener(`touchend`,X),window.addEventListener(`touchcancel`,X),P.current=window.setTimeout(()=>{P.current=null,F.current=!0,b.current=e,S(e),navigator.vibrate?.(10),document.body.style.touchAction=`none`,document.body.style.overscrollBehavior=`none`,me(e),z.current??=requestAnimationFrame(Y)},ee))}function me(t){let n=v.current;if(!n)return;let r=y.current.indexOf(t),i=n.querySelector(`[data-index="${r}"]`);if(!i)return;let a=i.getBoundingClientRect();H.current=R.current-a.top,U.current=a.left,W.current=a.height;let o=i.cloneNode(!0);o.removeAttribute(`data-index`),o.setAttribute(`data-drag-clone`,`true`),o.setAttribute(`aria-hidden`,`true`),o.classList.add(e.virtualListDragClone),Object.assign(o.style,{position:`fixed`,left:`0`,top:`0`,width:`${a.width}px`,height:`${a.height}px`,margin:`0`,opacity:`1`,pointerEvents:`none`,zIndex:`3`,background:`var(--background)`,boxShadow:`var(--card-shadow)`,borderRadius:`var(--default-border-radius)`}),n.appendChild(o),V.current=o,J()}a(()=>X,[X]);let Q=l||u,$=Q&&!G;function he(e,t){let n=u&&C?.key===t?C.position:void 0;return{reorderable:Q&&!p,mouseDragEnabled:$,draggable:$&&(!p||t===N)||void 0,isDragging:Q&&t===x,onMouseDown:Q&&p?e=>ie(t,e):void 0,onMouseOver:Q&&p?e=>ne(t,e):void 0,onMouseLeave:Q&&p?re:void 0,onDragStart:Q?e=>ae(t,e):void 0,onDragOver:u?t=>se(e,t):l?t=>de(e,t):void 0,onDragEnd:u?ue:l?fe:void 0,onDrop:u?le:void 0,onTouchStart:Q?e=>pe(t,e):void 0,dropPosition:n,dropIndentPx:n?(g[e]??0)*m:void 0}}function ge(){return B.current?(B.current=!1,!0):!1}return{draggingKey:x,dragEnabled:Q,getRowDragProps:he,consumeSuppressedClick:ge}}export{l as useRowDrag};
|
|
2
2
|
//# sourceMappingURL=useRowDrag.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRowDrag.mjs","names":[],"sources":["../../../../package/common/virtualList/useRowDrag.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';\n\nimport styles from './virtualList.module.pcss';\nimport { resolveReorderTarget, resolveTreeDropTarget, lastDescendantIndex } from './virtualList.utils';\nimport type { TreeDropPosition, TreeDropTarget } from './virtualList.types';\n\ninterface UseRowDragArgs {\n reorderable: boolean;\n treeReorder: boolean;\n onReorder?: (fromIndex: number, toIndex: number) => void;\n onTreeDrop?: (dragKey: string | number, target: TreeDropTarget) => void;\n longPressDelay: number;\n dragHandle: boolean;\n treeIndentPx: number;\n /** Current flat ordering and depth tags (mouse handlers read these directly). */\n keys: (string | number)[];\n depths: number[];\n isGroupAt: (index: number) => boolean;\n rootRef: React.RefObject<HTMLDivElement | null>;\n /** Latest keys for async (touch / rAF) consumers — owned by useVirtualWindow. */\n keysRef: React.RefObject<(string | number)[]>;\n}\n\n/** The drag-related props the parent spreads onto a given row. */\nexport interface RowDragProps {\n reorderable: boolean;\n draggable: boolean | undefined;\n isDragging: boolean;\n onMouseDown: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n onDragStart: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onDragOver: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onDragEnd: (() => void) | undefined;\n onDrop: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onTouchStart: ((e: React.TouchEvent<HTMLDivElement>) => void) | undefined;\n dropPosition: TreeDropPosition | undefined;\n dropIndentPx: number | undefined;\n}\n\ninterface UseRowDragResult {\n /** True while a row is being dragged (for the root reordering style). */\n draggingKey: string | number | null;\n /** Whether either drag mode is active. */\n dragEnabled: boolean;\n /** Drag props for the row at `index` / `key`. */\n getRowDragProps: (index: number, key: string | number) => RowDragProps;\n /** Returns true (and resets) if the next click should be swallowed (post-drag). */\n consumeSuppressedClick: () => boolean;\n}\n\n/**\n * Row drag-and-drop for both modes:\n * - `reorderable` (flat): native HTML5 drag on desktop; rows move live via\n * `onReorder` with midpoint hysteresis.\n * - `treeReorder`: a drop indicator (before/after/inside) is shown during the\n * drag and `onTreeDrop` fires once on release; dropping onto the dragged\n * node's own subtree is forbidden.\n * Touch uses a long-press → lifted-clone → pointer-move path with edge\n * auto-scroll. `dragHandle` restricts initiation to `[data-drag-handle]`.\n */\nexport function useRowDrag({\n reorderable,\n treeReorder,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n}: UseRowDragArgs): UseRowDragResult {\n // The dragged item is tracked by its stable key (indices shift after each\n // live reorder); the ref drives the logic, the state drives the row's style.\n const dragKeyRef = useRef<string | number | null>(null);\n const [draggingKey, setDraggingKey] = useState<string | number | null>(null);\n // Tree-reorder drop indicator. Unlike flat reorder (which moves rows live),\n // tree mode shows where the drop will land and commits once on release.\n const [dropTarget, setDropTarget] = useState<TreeDropTarget | null>(null);\n const dropTargetRef = useRef<TreeDropTarget | null>(null);\n const onReorderRef = useRef(onReorder);\n const onTreeDropRef = useRef(onTreeDrop);\n const treeModeRef = useRef(treeReorder);\n const depthsRef = useRef<number[]>(depths);\n const isGroupAtRef = useRef<(i: number) => boolean>(isGroupAt);\n // When `dragHandle` is on, a mousedown sets this to whether it landed on a\n // `[data-drag-handle]` element, gating whether the ensuing dragstart is honoured.\n const dragFromHandleRef = useRef(true);\n const longPressRef = useRef<number | null>(null);\n const touchActiveRef = useRef(false);\n const touchStartYRef = useRef<number | null>(null);\n const pointerXRef = useRef(0);\n const pointerYRef = useRef(0);\n const autoScrollRef = useRef<number | null>(null);\n const suppressNextClickRef = useRef(false);\n // Floating clone that follows the finger during a touch drag.\n const dragCloneRef = useRef<HTMLElement | null>(null);\n const grabOffsetYRef = useRef(0);\n const cloneLeftRef = useRef(0);\n const dragHeightRef = useRef(0);\n // The native `draggable` attribute (mouse DnD) makes a long-press start the\n // browser's own drag on touch devices, which hijacks our touch handlers. So\n // expose it only where the primary pointer is fine (mouse/trackpad); touch\n // devices use the touch handlers below instead.\n const [isCoarsePointer] = useState(() =>\n typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(pointer: coarse)').matches\n : false,\n );\n\n useLayoutEffect(() => {\n onReorderRef.current = onReorder;\n onTreeDropRef.current = onTreeDrop;\n treeModeRef.current = treeReorder;\n depthsRef.current = depths;\n dropTargetRef.current = dropTarget;\n isGroupAtRef.current = isGroupAt;\n });\n\n // ── Mouse / shared start ──────────────────────────────────────────────────\n\n // With `dragHandle`, remember on mousedown whether the press landed on a\n // handle, so the dragstart that follows can be ignored if it didn't.\n function handleRowMouseDown(e: React.MouseEvent<HTMLDivElement>) {\n dragFromHandleRef.current = !!(e.target as HTMLElement).closest('[data-drag-handle]');\n }\n\n function handleReorderStart(key: string | number, e: React.DragEvent<HTMLDivElement>) {\n if (dragHandle && !dragFromHandleRef.current) {\n e.preventDefault(); // not started from a handle — let the row stay put\n return;\n }\n dragKeyRef.current = key;\n setDraggingKey(key);\n if (treeReorder) setDropTarget(null);\n }\n\n // ── Tree mode ─────────────────────────────────────────────────────────────\n\n /** Resolve the tree drop target for the row at `overIndex` under pointer `y`. */\n function computeTreeDrop(overIndex: number, y: number, rect: DOMRect): TreeDropTarget | null {\n const dragKey = dragKeyRef.current;\n if (dragKey == null) return null;\n const fromIndex = keys.indexOf(dragKey);\n // Forbid dropping onto the dragged node or anything in its own subtree.\n if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, depths)) {\n return null;\n }\n const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAt(overIndex));\n return { key: keys[overIndex], position };\n }\n\n function handleTreeDragOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault(); // allow the drop\n const rect = e.currentTarget.getBoundingClientRect();\n const next = computeTreeDrop(overIndex, e.clientY, rect);\n setDropTarget((prev) =>\n prev?.key === next?.key && prev?.position === next?.position ? prev : next,\n );\n }\n\n function commitTreeDrop() {\n const dragKey = dragKeyRef.current;\n const target = dropTargetRef.current;\n if (dragKey != null && target && target.key !== dragKey) onTreeDrop?.(dragKey, target);\n }\n\n function handleTreeDrop(e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault();\n commitTreeDrop();\n setDropTarget(null);\n dragKeyRef.current = null;\n setDraggingKey(null);\n }\n\n function handleTreeDragEnd() {\n // Fires after `drop` (or on its own when released off a valid target).\n setDropTarget(null);\n dragKeyRef.current = null;\n setDraggingKey(null);\n }\n\n // ── Flat mode ─────────────────────────────────────────────────────────────\n\n function handleReorderOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault(); // allow the drop\n const dragKey = dragKeyRef.current;\n if (dragKey == null) return;\n const fromIndex = keys.indexOf(dragKey);\n // Midpoint hysteresis (see resolveReorderTarget): a move only happens once\n // the pointer crosses the centre of the row it's over. After a swap the\n // dragged row sits under the cursor, so moving again needs real pointer\n // travel — this stops the oscillation that made dragging feel jumpy.\n const rect = e.currentTarget.getBoundingClientRect();\n const target = resolveReorderTarget(fromIndex, overIndex, e.clientY, rect.top, rect.height);\n if (target != null) onReorder?.(fromIndex, target);\n }\n\n function handleReorderEnd() {\n dragKeyRef.current = null;\n setDraggingKey(null);\n }\n\n // ── Touch reordering (long-press to arm, then drag) ──────────────────────────\n // These handlers run from window listeners during a touch drag, so they read\n // mutable state from refs and are stable across renders.\n\n const reorderAtPoint = useCallback((x: number) => {\n const dragKey = dragKeyRef.current;\n const root = rootRef.current;\n if (dragKey == null || !root) return;\n // Reorder against the centre of the lifted clone, not the raw finger — the\n // finger sits below the clone's top by the grab offset, so using it would\n // make the top (and bottom) rows unreachable. The clone is what the user\n // aims with, so match it.\n const y = dragCloneRef.current\n ? pointerYRef.current - grabOffsetYRef.current + dragHeightRef.current / 2\n : pointerYRef.current;\n const el = document.elementFromPoint(x, y) as HTMLElement | null;\n const rowEl = el && root.contains(el) ? el.closest<HTMLElement>('[data-index]') : null;\n if (!rowEl) return;\n const overIndex = Number(rowEl.dataset.index);\n if (Number.isNaN(overIndex)) return;\n const fromIndex = keysRef.current.indexOf(dragKey);\n const rect = rowEl.getBoundingClientRect();\n if (treeModeRef.current) {\n // Tree mode: don't move live — just track where the drop will land.\n const ks = keysRef.current;\n const ds = depthsRef.current;\n if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, ds)) {\n setDropTarget(null);\n return;\n }\n const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAtRef.current(overIndex));\n const next: TreeDropTarget = { key: ks[overIndex], position };\n setDropTarget((prev) => (prev?.key === next.key && prev?.position === next.position ? prev : next));\n return;\n }\n const target = resolveReorderTarget(fromIndex, overIndex, y, rect.top, rect.height);\n if (target != null) onReorderRef.current?.(fromIndex, target);\n }, [rootRef, keysRef]);\n\n // Keep the floating clone glued under the finger (same grab point).\n const positionDragClone = useCallback(() => {\n const clone = dragCloneRef.current;\n if (!clone) return;\n clone.style.transform = `translate(${cloneLeftRef.current}px, ${pointerYRef.current - grabOffsetYRef.current}px)`;\n }, []);\n\n // While the finger sits near a top/bottom edge (and within the list's width),\n // scroll the list and keep reordering against the rows now passing under it.\n const autoScrollTick = useCallback(() => {\n const root = rootRef.current;\n if (!root || !touchActiveRef.current) { autoScrollRef.current = null; return; }\n const rect = root.getBoundingClientRect();\n const x = pointerXRef.current;\n const y = pointerYRef.current;\n const EDGE = 48;\n let dy = 0;\n // Only auto-scroll while the finger is actually over the list — moving it\n // outside (any direction) stops the scroll instead of running away; pinning\n // the dragged row keeps touch events alive so it resumes when you come back.\n const inside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;\n if (inside) {\n if (y < rect.top + EDGE) dy = -Math.min(18, Math.ceil((rect.top + EDGE - y) / 3));\n else if (y > rect.bottom - EDGE) dy = Math.min(18, Math.ceil((y - (rect.bottom - EDGE)) / 3));\n }\n if (dy !== 0) {\n root.scrollTop += dy;\n reorderAtPoint(x);\n }\n autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n }, [reorderAtPoint, rootRef]);\n\n const endTouchDrag = useCallback(() => {\n if (longPressRef.current != null) { clearTimeout(longPressRef.current); longPressRef.current = null; }\n window.removeEventListener('touchmove', handleTouchMove);\n window.removeEventListener('touchend', endTouchDrag);\n window.removeEventListener('touchcancel', endTouchDrag);\n if (autoScrollRef.current != null) { cancelAnimationFrame(autoScrollRef.current); autoScrollRef.current = null; }\n if (dragCloneRef.current) { dragCloneRef.current.remove(); dragCloneRef.current = null; }\n // Restore native scrolling that was locked for the duration of the drag.\n document.body.style.touchAction = '';\n document.body.style.overscrollBehavior = '';\n if (touchActiveRef.current) {\n // A real drag happened — swallow the click the browser fires afterwards\n // so it doesn't, e.g., toggle row selection.\n suppressNextClickRef.current = true;\n setTimeout(() => { suppressNextClickRef.current = false; }, 350);\n // Tree mode commits the pending drop on release (flat mode already moved\n // rows live during the drag, so there's nothing to commit here).\n if (treeModeRef.current) {\n const dk = dragKeyRef.current;\n const target = dropTargetRef.current;\n if (dk != null && target && target.key !== dk) onTreeDropRef.current?.(dk, target);\n }\n }\n setDropTarget(null);\n touchActiveRef.current = false;\n touchStartYRef.current = null;\n dragKeyRef.current = null;\n setDraggingKey(null);\n }, []);\n\n const handleTouchMove = useCallback((e: TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n pointerXRef.current = t.clientX;\n pointerYRef.current = t.clientY;\n if (!touchActiveRef.current) {\n // Before the long-press fires: if the finger travels, the user means to\n // scroll, so disarm and let the browser handle it (we don't preventDefault).\n const sy = touchStartYRef.current;\n if (sy != null && Math.abs(t.clientY - sy) > 10 && longPressRef.current != null) {\n clearTimeout(longPressRef.current);\n longPressRef.current = null;\n }\n return;\n }\n // Non-passive listener: preventDefault cancels the native scroll for this\n // gesture — the only reliable way once touch-action has been latched at\n // touchstart. This is what makes the drag actually move on touch devices.\n if (e.cancelable) e.preventDefault();\n positionDragClone();\n reorderAtPoint(t.clientX);\n }, [reorderAtPoint, positionDragClone]);\n\n function handleRowTouchStart(key: string | number, e: React.TouchEvent<HTMLDivElement>) {\n if (!reorderable && !treeReorder) return;\n // With a drag handle, only a press that starts on it arms the drag, so the\n // rest of the row stays free for tapping/typing into inputs.\n if (dragHandle && !(e.target as HTMLElement).closest('[data-drag-handle]')) return;\n if (longPressRef.current != null || touchActiveRef.current) return; // ignore extra fingers\n const t = e.touches[0];\n if (!t) return;\n touchStartYRef.current = t.clientY;\n pointerXRef.current = t.clientX;\n pointerYRef.current = t.clientY;\n window.addEventListener('touchmove', handleTouchMove, { passive: false });\n window.addEventListener('touchend', endTouchDrag);\n window.addEventListener('touchcancel', endTouchDrag);\n longPressRef.current = window.setTimeout(() => {\n longPressRef.current = null;\n touchActiveRef.current = true;\n dragKeyRef.current = key;\n setDraggingKey(key);\n navigator.vibrate?.(10);\n // Lock native scrolling for the whole gesture — relying on per-move\n // preventDefault isn't enough once the finger leaves and re-enters the\n // list (the browser commits to a scroll and ignores it). We do all the\n // scrolling ourselves via autoScrollTick.\n document.body.style.touchAction = 'none';\n document.body.style.overscrollBehavior = 'none';\n createDragClone(key);\n if (autoScrollRef.current == null) autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n }, longPressDelay);\n }\n\n // Build a floating copy of the picked-up row that tracks the finger, so the\n // drag has a visible \"lifted\" element (the original row stays dimmed in place\n // as a drop placeholder).\n function createDragClone(key: string | number) {\n const root = rootRef.current;\n if (!root) return;\n const idx = keysRef.current.indexOf(key);\n const src = root.querySelector<HTMLElement>(`[data-index=\"${idx}\"]`);\n if (!src) return;\n const r = src.getBoundingClientRect();\n grabOffsetYRef.current = pointerYRef.current - r.top;\n cloneLeftRef.current = r.left;\n dragHeightRef.current = r.height;\n const clone = src.cloneNode(true) as HTMLElement;\n clone.removeAttribute('data-index');\n clone.setAttribute('data-drag-clone', 'true');\n clone.setAttribute('aria-hidden', 'true');\n clone.classList.add(styles.virtualListDragClone);\n Object.assign(clone.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n width: `${r.width}px`,\n height: `${r.height}px`,\n margin: '0',\n opacity: '1',\n pointerEvents: 'none',\n // Above the sticky header (z-index 2) and any focused row (z-index 1).\n zIndex: '3',\n background: 'var(--background)',\n boxShadow: 'var(--card-shadow)',\n borderRadius: 'var(--default-border-radius)',\n } as Partial<CSSStyleDeclaration>);\n // Appended inside the list so it inherits the active theme's CSS variables;\n // position:fixed keeps it in viewport coordinates regardless.\n root.appendChild(clone);\n dragCloneRef.current = clone;\n positionDragClone();\n }\n\n // Tear down any in-flight touch drag if the component unmounts mid-gesture.\n useEffect(() => endTouchDrag, [endTouchDrag]);\n\n // Either drag mode arms the same row gestures; the handlers above branch on\n // `treeReorder` for the differing target resolution / commit behaviour.\n const dragEnabled = reorderable || treeReorder;\n\n function getRowDragProps(index: number, key: string | number): RowDragProps {\n // The drop indicator only renders on the row currently targeted. `before`/\n // `after` lines indent to the target's depth so they sit under where the\n // dropped content will start; `inside` highlights the whole row instead.\n const rowDrop = treeReorder && dropTarget?.key === key ? dropTarget.position : undefined;\n return {\n // Drives the row-wide grab cursor. With `dragHandle`, only the handle\n // initiates a drag, so the cursor belongs on the handle (styled by the\n // consumer), not across the whole row.\n reorderable: dragEnabled && !dragHandle,\n draggable: (dragEnabled && !isCoarsePointer) || undefined,\n isDragging: dragEnabled && key === draggingKey,\n onMouseDown: dragEnabled && dragHandle ? handleRowMouseDown : undefined,\n onDragStart: dragEnabled ? (e) => handleReorderStart(key, e) : undefined,\n onDragOver:\n treeReorder ? (e) => handleTreeDragOver(index, e)\n : reorderable ? (e) => handleReorderOver(index, e)\n : undefined,\n onDragEnd: treeReorder ? handleTreeDragEnd : reorderable ? handleReorderEnd : undefined,\n onDrop: treeReorder ? handleTreeDrop : undefined,\n onTouchStart: dragEnabled ? (e) => handleRowTouchStart(key, e) : undefined,\n dropPosition: rowDrop,\n dropIndentPx: rowDrop ? (depths[index] ?? 0) * treeIndentPx : undefined,\n };\n }\n\n function consumeSuppressedClick(): boolean {\n if (suppressNextClickRef.current) { suppressNextClickRef.current = false; return true; }\n return false;\n }\n\n return { draggingKey, dragEnabled, getRowDragProps, consumeSuppressedClick };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useRowDrag.mjs","names":[],"sources":["../../../../package/common/virtualList/useRowDrag.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';\n\nimport styles from './virtualList.module.pcss';\nimport { resolveReorderTarget, resolveTreeDropTarget, lastDescendantIndex } from './virtualList.utils';\nimport type { TreeDropPosition, TreeDropTarget } from './virtualList.types';\n\ninterface UseRowDragArgs {\n reorderable: boolean;\n treeReorder: boolean;\n onReorder?: (fromIndex: number, toIndex: number) => void;\n onTreeDrop?: (dragKey: string | number, target: TreeDropTarget) => void;\n longPressDelay: number;\n dragHandle: boolean;\n treeIndentPx: number;\n /** Current flat ordering and depth tags (mouse handlers read these directly). */\n keys: (string | number)[];\n depths: number[];\n isGroupAt: (index: number) => boolean;\n rootRef: React.RefObject<HTMLDivElement | null>;\n /** Latest keys for async (touch / rAF) consumers — owned by useVirtualWindow. */\n keysRef: React.RefObject<(string | number)[]>;\n}\n\n/** The drag-related props the parent spreads onto a given row. */\nexport interface RowDragProps {\n reorderable: boolean;\n /** Native mouse DnD is active for the list (independent of which row is armed). */\n mouseDragEnabled: boolean;\n draggable: boolean | undefined;\n isDragging: boolean;\n onMouseDown: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n onMouseOver: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n onMouseLeave: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n onDragStart: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onDragOver: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onDragEnd: (() => void) | undefined;\n onDrop: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n onTouchStart: ((e: React.TouchEvent<HTMLDivElement>) => void) | undefined;\n dropPosition: TreeDropPosition | undefined;\n dropIndentPx: number | undefined;\n}\n\ninterface UseRowDragResult {\n /** True while a row is being dragged (for the root reordering style). */\n draggingKey: string | number | null;\n /** Whether either drag mode is active. */\n dragEnabled: boolean;\n /** Drag props for the row at `index` / `key`. */\n getRowDragProps: (index: number, key: string | number) => RowDragProps;\n /** Returns true (and resets) if the next click should be swallowed (post-drag). */\n consumeSuppressedClick: () => boolean;\n}\n\n/**\n * Row drag-and-drop for both modes:\n * - `reorderable` (flat): native HTML5 drag on desktop; rows move live via\n * `onReorder` with midpoint hysteresis.\n * - `treeReorder`: a drop indicator (before/after/inside) is shown during the\n * drag and `onTreeDrop` fires once on release; dropping onto the dragged\n * node's own subtree is forbidden.\n * Touch uses a long-press → lifted-clone → pointer-move path with edge\n * auto-scroll. `dragHandle` restricts initiation to `[data-drag-handle]`.\n */\nexport function useRowDrag({\n reorderable,\n treeReorder,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n}: UseRowDragArgs): UseRowDragResult {\n // The dragged item is tracked by its stable key (indices shift after each\n // live reorder); the ref drives the logic, the state drives the row's style.\n const dragKeyRef = useRef<string | number | null>(null);\n const [draggingKey, setDraggingKey] = useState<string | number | null>(null);\n // Tree-reorder drop indicator. Unlike flat reorder (which moves rows live),\n // tree mode shows where the drop will land and commits once on release.\n const [dropTarget, setDropTarget] = useState<TreeDropTarget | null>(null);\n const dropTargetRef = useRef<TreeDropTarget | null>(null);\n const onReorderRef = useRef(onReorder);\n const onTreeDropRef = useRef(onTreeDrop);\n const treeModeRef = useRef(treeReorder);\n const depthsRef = useRef<number[]>(depths);\n const isGroupAtRef = useRef<(i: number) => boolean>(isGroupAt);\n // When `dragHandle` is on, a mousedown sets this to whether it landed on a\n // `[data-drag-handle]` element, gating whether the ensuing dragstart is honoured.\n const dragFromHandleRef = useRef(true);\n // In `dragHandle` mode the `draggable` attribute is only on the row whose\n // handle the pointer is currently over (\"armed\"). A permanently `draggable`\n // row breaks mouse text selection (caret placement, double-click word select,\n // drag select) inside any child input in Firefox — see Mozilla bugs 739071 /\n // 800050 / 1189486. Arming on hover rather than on mousedown means the\n // attribute is already in place before the press, so the browser never has to\n // notice it appearing mid-gesture, and any pointer move re-derives it.\n const armedKeyRef = useRef<string | number | null>(null);\n const [armedKey, setArmedKeyState] = useState<string | number | null>(null);\n const longPressRef = useRef<number | null>(null);\n const touchActiveRef = useRef(false);\n const touchStartYRef = useRef<number | null>(null);\n const pointerXRef = useRef(0);\n const pointerYRef = useRef(0);\n const autoScrollRef = useRef<number | null>(null);\n const suppressNextClickRef = useRef(false);\n // Floating clone that follows the finger during a touch drag.\n const dragCloneRef = useRef<HTMLElement | null>(null);\n const grabOffsetYRef = useRef(0);\n const cloneLeftRef = useRef(0);\n const dragHeightRef = useRef(0);\n // The native `draggable` attribute (mouse DnD) makes a long-press start the\n // browser's own drag on touch devices, which hijacks our touch handlers. So\n // expose it only where the primary pointer is fine (mouse/trackpad); touch\n // devices use the touch handlers below instead.\n const [isCoarsePointer] = useState(() =>\n typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(pointer: coarse)').matches\n : false,\n );\n\n useLayoutEffect(() => {\n onReorderRef.current = onReorder;\n onTreeDropRef.current = onTreeDrop;\n treeModeRef.current = treeReorder;\n depthsRef.current = depths;\n dropTargetRef.current = dropTarget;\n isGroupAtRef.current = isGroupAt;\n });\n\n // ── Mouse / shared start ──────────────────────────────────────────────────\n\n /** Move the `draggable` attribute to `next` (or off entirely for `null`). */\n function arm(next: string | number | null) {\n // Arming exists purely to place the native `draggable` attribute, which is\n // never set on a coarse pointer (touch uses the handlers below instead). A\n // tap still synthesises mouseover/mousedown, so without this a touch device\n // would re-render the list on every handle tap to no effect whatsoever.\n if (isCoarsePointer) return;\n if (armedKeyRef.current === next) return; // mouseover fires constantly — no-op\n armedKeyRef.current = next;\n setArmedKeyState(next);\n }\n\n // Arm while the pointer rests on this row's handle, disarm as it moves off.\n // `buttons !== 0` means a press is already in progress: the pointer can drift\n // off the handle before the browser reaches its drag threshold, so hover is\n // ignored until release rather than disarming a drag that is about to start.\n function handleRowMouseOver(key: string | number, e: React.MouseEvent<HTMLDivElement>) {\n if (e.buttons !== 0) return;\n arm((e.target as HTMLElement).closest('[data-drag-handle]') ? key : null);\n }\n\n function handleRowMouseLeave(e: React.MouseEvent<HTMLDivElement>) {\n if (e.buttons !== 0) return;\n arm(null);\n }\n\n // With `dragHandle`, remember on mousedown whether the press landed on a\n // handle, so the dragstart that follows can be ignored if it didn't. Arming\n // here too covers a handle that came to sit under an already-stationary cursor\n // (the list scrolled), which produces no mouseover.\n function handleRowMouseDown(key: string | number, e: React.MouseEvent<HTMLDivElement>) {\n if (e.button !== 0) return; // only a primary press can begin a drag\n const fromHandle = !!(e.target as HTMLElement).closest('[data-drag-handle]');\n dragFromHandleRef.current = fromHandle;\n if (fromHandle) arm(key);\n }\n\n function handleReorderStart(key: string | number, e: React.DragEvent<HTMLDivElement>) {\n if (dragHandle && !dragFromHandleRef.current) {\n e.preventDefault(); // not started from a handle — let the row stay put\n return;\n }\n dragKeyRef.current = key;\n setDraggingKey(key);\n if (treeReorder) setDropTarget(null);\n }\n\n // ── Tree mode ─────────────────────────────────────────────────────────────\n\n /** Resolve the tree drop target for the row at `overIndex` under pointer `y`. */\n function computeTreeDrop(overIndex: number, y: number, rect: DOMRect): TreeDropTarget | null {\n const dragKey = dragKeyRef.current;\n if (dragKey == null) return null;\n const fromIndex = keys.indexOf(dragKey);\n // Forbid dropping onto the dragged node or anything in its own subtree.\n if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, depths)) {\n return null;\n }\n const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAt(overIndex));\n return { key: keys[overIndex], position };\n }\n\n function handleTreeDragOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault(); // allow the drop\n const rect = e.currentTarget.getBoundingClientRect();\n const next = computeTreeDrop(overIndex, e.clientY, rect);\n setDropTarget((prev) =>\n prev?.key === next?.key && prev?.position === next?.position ? prev : next,\n );\n }\n\n function commitTreeDrop() {\n const dragKey = dragKeyRef.current;\n const target = dropTargetRef.current;\n if (dragKey != null && target && target.key !== dragKey) onTreeDrop?.(dragKey, target);\n }\n\n function handleTreeDrop(e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault();\n commitTreeDrop();\n setDropTarget(null);\n dragKeyRef.current = null;\n setDraggingKey(null);\n arm(null);\n }\n\n function handleTreeDragEnd() {\n // Fires after `drop` (or on its own when released off a valid target).\n setDropTarget(null);\n dragKeyRef.current = null;\n setDraggingKey(null);\n arm(null);\n }\n\n // ── Flat mode ─────────────────────────────────────────────────────────────\n\n function handleReorderOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n e.preventDefault(); // allow the drop\n const dragKey = dragKeyRef.current;\n if (dragKey == null) return;\n const fromIndex = keys.indexOf(dragKey);\n // Midpoint hysteresis (see resolveReorderTarget): a move only happens once\n // the pointer crosses the centre of the row it's over. After a swap the\n // dragged row sits under the cursor, so moving again needs real pointer\n // travel — this stops the oscillation that made dragging feel jumpy.\n const rect = e.currentTarget.getBoundingClientRect();\n const target = resolveReorderTarget(fromIndex, overIndex, e.clientY, rect.top, rect.height);\n if (target != null) onReorder?.(fromIndex, target);\n }\n\n function handleReorderEnd() {\n dragKeyRef.current = null;\n setDraggingKey(null);\n arm(null);\n }\n\n // ── Touch reordering (long-press to arm, then drag) ──────────────────────────\n // These handlers run from window listeners during a touch drag, so they read\n // mutable state from refs and are stable across renders.\n\n const reorderAtPoint = useCallback((x: number) => {\n const dragKey = dragKeyRef.current;\n const root = rootRef.current;\n if (dragKey == null || !root) return;\n // Reorder against the centre of the lifted clone, not the raw finger — the\n // finger sits below the clone's top by the grab offset, so using it would\n // make the top (and bottom) rows unreachable. The clone is what the user\n // aims with, so match it.\n const y = dragCloneRef.current\n ? pointerYRef.current - grabOffsetYRef.current + dragHeightRef.current / 2\n : pointerYRef.current;\n const el = document.elementFromPoint(x, y) as HTMLElement | null;\n const rowEl = el && root.contains(el) ? el.closest<HTMLElement>('[data-index]') : null;\n if (!rowEl) return;\n const overIndex = Number(rowEl.dataset.index);\n if (Number.isNaN(overIndex)) return;\n const fromIndex = keysRef.current.indexOf(dragKey);\n const rect = rowEl.getBoundingClientRect();\n if (treeModeRef.current) {\n // Tree mode: don't move live — just track where the drop will land.\n const ks = keysRef.current;\n const ds = depthsRef.current;\n if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, ds)) {\n setDropTarget(null);\n return;\n }\n const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAtRef.current(overIndex));\n const next: TreeDropTarget = { key: ks[overIndex], position };\n setDropTarget((prev) => (prev?.key === next.key && prev?.position === next.position ? prev : next));\n return;\n }\n const target = resolveReorderTarget(fromIndex, overIndex, y, rect.top, rect.height);\n if (target != null) onReorderRef.current?.(fromIndex, target);\n }, [rootRef, keysRef]);\n\n // Keep the floating clone glued under the finger (same grab point).\n const positionDragClone = useCallback(() => {\n const clone = dragCloneRef.current;\n if (!clone) return;\n clone.style.transform = `translate(${cloneLeftRef.current}px, ${pointerYRef.current - grabOffsetYRef.current}px)`;\n }, []);\n\n // While the finger sits near a top/bottom edge (and within the list's width),\n // scroll the list and keep reordering against the rows now passing under it.\n const autoScrollTick = useCallback(() => {\n const root = rootRef.current;\n if (!root || !touchActiveRef.current) { autoScrollRef.current = null; return; }\n const rect = root.getBoundingClientRect();\n const x = pointerXRef.current;\n const y = pointerYRef.current;\n const EDGE = 48;\n let dy = 0;\n // Only auto-scroll while the finger is actually over the list — moving it\n // outside (any direction) stops the scroll instead of running away; pinning\n // the dragged row keeps touch events alive so it resumes when you come back.\n const inside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;\n if (inside) {\n if (y < rect.top + EDGE) dy = -Math.min(18, Math.ceil((rect.top + EDGE - y) / 3));\n else if (y > rect.bottom - EDGE) dy = Math.min(18, Math.ceil((y - (rect.bottom - EDGE)) / 3));\n }\n if (dy !== 0) {\n root.scrollTop += dy;\n reorderAtPoint(x);\n }\n autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n }, [reorderAtPoint, rootRef]);\n\n const endTouchDrag = useCallback(() => {\n if (longPressRef.current != null) { clearTimeout(longPressRef.current); longPressRef.current = null; }\n window.removeEventListener('touchmove', handleTouchMove);\n window.removeEventListener('touchend', endTouchDrag);\n window.removeEventListener('touchcancel', endTouchDrag);\n if (autoScrollRef.current != null) { cancelAnimationFrame(autoScrollRef.current); autoScrollRef.current = null; }\n if (dragCloneRef.current) { dragCloneRef.current.remove(); dragCloneRef.current = null; }\n // Restore native scrolling that was locked for the duration of the drag.\n document.body.style.touchAction = '';\n document.body.style.overscrollBehavior = '';\n if (touchActiveRef.current) {\n // A real drag happened — swallow the click the browser fires afterwards\n // so it doesn't, e.g., toggle row selection.\n suppressNextClickRef.current = true;\n setTimeout(() => { suppressNextClickRef.current = false; }, 350);\n // Tree mode commits the pending drop on release (flat mode already moved\n // rows live during the drag, so there's nothing to commit here).\n if (treeModeRef.current) {\n const dk = dragKeyRef.current;\n const target = dropTargetRef.current;\n if (dk != null && target && target.key !== dk) onTreeDropRef.current?.(dk, target);\n }\n }\n setDropTarget(null);\n touchActiveRef.current = false;\n touchStartYRef.current = null;\n dragKeyRef.current = null;\n setDraggingKey(null);\n }, []);\n\n const handleTouchMove = useCallback((e: TouchEvent) => {\n const t = e.touches[0];\n if (!t) return;\n pointerXRef.current = t.clientX;\n pointerYRef.current = t.clientY;\n if (!touchActiveRef.current) {\n // Before the long-press fires: if the finger travels, the user means to\n // scroll, so disarm and let the browser handle it (we don't preventDefault).\n const sy = touchStartYRef.current;\n if (sy != null && Math.abs(t.clientY - sy) > 10 && longPressRef.current != null) {\n clearTimeout(longPressRef.current);\n longPressRef.current = null;\n }\n return;\n }\n // Non-passive listener: preventDefault cancels the native scroll for this\n // gesture — the only reliable way once touch-action has been latched at\n // touchstart. This is what makes the drag actually move on touch devices.\n if (e.cancelable) e.preventDefault();\n positionDragClone();\n reorderAtPoint(t.clientX);\n }, [reorderAtPoint, positionDragClone]);\n\n function handleRowTouchStart(key: string | number, e: React.TouchEvent<HTMLDivElement>) {\n if (!reorderable && !treeReorder) return;\n // With a drag handle, only a press that starts on it arms the drag, so the\n // rest of the row stays free for tapping/typing into inputs.\n if (dragHandle && !(e.target as HTMLElement).closest('[data-drag-handle]')) return;\n if (longPressRef.current != null || touchActiveRef.current) return; // ignore extra fingers\n const t = e.touches[0];\n if (!t) return;\n touchStartYRef.current = t.clientY;\n pointerXRef.current = t.clientX;\n pointerYRef.current = t.clientY;\n window.addEventListener('touchmove', handleTouchMove, { passive: false });\n window.addEventListener('touchend', endTouchDrag);\n window.addEventListener('touchcancel', endTouchDrag);\n longPressRef.current = window.setTimeout(() => {\n longPressRef.current = null;\n touchActiveRef.current = true;\n dragKeyRef.current = key;\n setDraggingKey(key);\n navigator.vibrate?.(10);\n // Lock native scrolling for the whole gesture — relying on per-move\n // preventDefault isn't enough once the finger leaves and re-enters the\n // list (the browser commits to a scroll and ignores it). We do all the\n // scrolling ourselves via autoScrollTick.\n document.body.style.touchAction = 'none';\n document.body.style.overscrollBehavior = 'none';\n createDragClone(key);\n if (autoScrollRef.current == null) autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n }, longPressDelay);\n }\n\n // Build a floating copy of the picked-up row that tracks the finger, so the\n // drag has a visible \"lifted\" element (the original row stays dimmed in place\n // as a drop placeholder).\n function createDragClone(key: string | number) {\n const root = rootRef.current;\n if (!root) return;\n const idx = keysRef.current.indexOf(key);\n const src = root.querySelector<HTMLElement>(`[data-index=\"${idx}\"]`);\n if (!src) return;\n const r = src.getBoundingClientRect();\n grabOffsetYRef.current = pointerYRef.current - r.top;\n cloneLeftRef.current = r.left;\n dragHeightRef.current = r.height;\n const clone = src.cloneNode(true) as HTMLElement;\n clone.removeAttribute('data-index');\n clone.setAttribute('data-drag-clone', 'true');\n clone.setAttribute('aria-hidden', 'true');\n clone.classList.add(styles.virtualListDragClone);\n Object.assign(clone.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n width: `${r.width}px`,\n height: `${r.height}px`,\n margin: '0',\n opacity: '1',\n pointerEvents: 'none',\n // Above the sticky header (z-index 2) and any focused row (z-index 1).\n zIndex: '3',\n background: 'var(--background)',\n boxShadow: 'var(--card-shadow)',\n borderRadius: 'var(--default-border-radius)',\n } as Partial<CSSStyleDeclaration>);\n // Appended inside the list so it inherits the active theme's CSS variables;\n // position:fixed keeps it in viewport coordinates regardless.\n root.appendChild(clone);\n dragCloneRef.current = clone;\n positionDragClone();\n }\n\n // Tear down any in-flight touch drag if the component unmounts mid-gesture.\n useEffect(() => endTouchDrag, [endTouchDrag]);\n\n // Either drag mode arms the same row gestures; the handlers above branch on\n // `treeReorder` for the differing target resolution / commit behaviour.\n const dragEnabled = reorderable || treeReorder;\n // Native mouse DnD is in play for this list. Row-level fallbacks that must\n // cover *every* row — swallowing the browser's own drop and context menu —\n // key off this, not off `draggable`, which in `dragHandle` mode is only ever\n // on the single armed row.\n const mouseDragEnabled = dragEnabled && !isCoarsePointer;\n\n function getRowDragProps(index: number, key: string | number): RowDragProps {\n // The drop indicator only renders on the row currently targeted. `before`/\n // `after` lines indent to the target's depth so they sit under where the\n // dropped content will start; `inside` highlights the whole row instead.\n const rowDrop = treeReorder && dropTarget?.key === key ? dropTarget.position : undefined;\n return {\n // Drives the row-wide grab cursor. With `dragHandle`, only the handle\n // initiates a drag, so the cursor belongs on the handle (styled by the\n // consumer), not across the whole row.\n reorderable: dragEnabled && !dragHandle,\n mouseDragEnabled,\n // In `dragHandle` mode the attribute only goes on the row the pointer has\n // armed, so rows stay text-selectable in Firefox the rest of the time.\n draggable: (mouseDragEnabled && (!dragHandle || key === armedKey)) || undefined,\n isDragging: dragEnabled && key === draggingKey,\n onMouseDown: dragEnabled && dragHandle ? (e) => handleRowMouseDown(key, e) : undefined,\n onMouseOver: dragEnabled && dragHandle ? (e) => handleRowMouseOver(key, e) : undefined,\n onMouseLeave: dragEnabled && dragHandle ? handleRowMouseLeave : undefined,\n onDragStart: dragEnabled ? (e) => handleReorderStart(key, e) : undefined,\n onDragOver:\n treeReorder ? (e) => handleTreeDragOver(index, e)\n : reorderable ? (e) => handleReorderOver(index, e)\n : undefined,\n onDragEnd: treeReorder ? handleTreeDragEnd : reorderable ? handleReorderEnd : undefined,\n onDrop: treeReorder ? handleTreeDrop : undefined,\n onTouchStart: dragEnabled ? (e) => handleRowTouchStart(key, e) : undefined,\n dropPosition: rowDrop,\n dropIndentPx: rowDrop ? (depths[index] ?? 0) * treeIndentPx : undefined,\n };\n }\n\n function consumeSuppressedClick(): boolean {\n if (suppressNextClickRef.current) { suppressNextClickRef.current = false; return true; }\n return false;\n }\n\n return { draggingKey, dragEnabled, getRowDragProps, consumeSuppressedClick };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{buildOffsets as e,findStartIndex as t,getVisibleRange as n}from"./virtualList.utils.mjs";import{useCallback as r,useEffect as i,useLayoutEffect as a,useMemo as o,useRef as s,useState as c}from"react";function l(e,t){if(typeof e==`number`)return e;let n=e.trim();if(n===``)return 0;let r=Number(n);if(!Number.isNaN(r))return r;let i=/^(-?\d*\.?\d+)px$/.exec(n);if(i)return parseFloat(i[1]);if(typeof document>`u`||!t)return 0;let a=document.createElement(`div`);a.style.cssText=`position:absolute;visibility:hidden;height:${n};`,t.appendChild(a);let o=a.getBoundingClientRect().height;return t.removeChild(a),Number.isFinite(o)?o:0}function u({items:u,keys:d,getItemKey:f,estimatedRowHeight:p,rowGap:m,overscan:h,onLoadMore:g,loadMoreThreshold:_}){let[
|
|
1
|
+
import{buildOffsets as e,findStartIndex as t,getVisibleRange as n}from"./virtualList.utils.mjs";import{useCallback as r,useEffect as i,useLayoutEffect as a,useMemo as o,useRef as s,useState as c}from"react";function l(e,t){if(typeof e==`number`)return e;let n=e.trim();if(n===``)return 0;let r=Number(n);if(!Number.isNaN(r))return r;let i=/^(-?\d*\.?\d+)px$/.exec(n);if(i)return parseFloat(i[1]);if(typeof document>`u`||!t)return 0;let a=document.createElement(`div`);a.style.cssText=`position:absolute;visibility:hidden;height:${n};`,t.appendChild(a);let o=a.getBoundingClientRect().height;return t.removeChild(a),Number.isFinite(o)?o:0}function u({items:u,keys:d,getItemKey:f,estimatedRowHeight:p,rowGap:m,overscan:h,onLoadMore:g,loadMoreThreshold:_,isLoading:v}){let[y,b]=c(0),[x,S]=c(0),[C,w]=c(0),[T,E]=c(()=>l(m,null)),[D,O]=c(new Map),[k,A]=c(!1),j=s(!1),M=s(null),N=s(null),P=s([]),F=s(null),I=s(0),L=s([]),[R]=c(()=>typeof ResizeObserver>`u`?null:new ResizeObserver(e=>{O(t=>{let n=null;for(let r of e){let e=r.target,i=Number(e.dataset.index);if(Number.isNaN(i))continue;let a=L.current[i];if(a===void 0)continue;let o=r.borderBoxSize?.[0],s=o?Math.round(o.blockSize):e.offsetHeight;t.get(a)!==s&&(n||=new Map(t),n.set(a,s))}return n??t})}));i(()=>()=>R?.disconnect(),[R]),a(()=>{let e=M.current;if(!e)return;S(e.clientHeight),w(e.clientWidth);let t=new ResizeObserver(()=>{S(e.clientHeight),w(e.clientWidth)});return t.observe(e),()=>t.disconnect()},[]);let z=s(g);a(()=>{z.current=g});let B=r(()=>{let e=z.current;!e||j.current||(j.current=!0,A(!0),e().finally(()=>{j.current=!1,A(!1)}))},[]),V=r(e=>{let t=e.currentTarget;I.current=t.scrollTop,F.current??=requestAnimationFrame(()=>{F.current=null,b(I.current)}),t.scrollTop+t.clientHeight>=t.scrollHeight-_&&B()},[_,B]);i(()=>()=>{F.current!=null&&cancelAnimationFrame(F.current)},[]),a(()=>{L.current=d}),a(()=>{let e=l(m,M.current);E(t=>t===e?t:e)},[m,C,x]);let{offsets:H,totalHeight:U}=o(()=>e(d,D,p,T),[d,D,p,T]),W=s(``);i(()=>{if(!g||v||k||x===0||U>=x-(N.current?.offsetHeight??0))return;let e=`${u.length}:${x}`;W.current!==e&&(W.current=e,B())},[g,v,k,u.length,U,x,B]),a(()=>{let e=M.current,n=P.current;if(P.current=H,!e||n.length===0)return;let r=e.scrollTop;if(r<=0)return;let i=t(n,r,n.length-1);if(i>=u.length)return;let a=r-n[i],o=H[i]+a;Math.abs(o-r)>.5&&(e.scrollTop=o,I.current=o,b(o))},[H,u.length]);let G=o(()=>{function e(e,t){let n=M.current;if(!n)return;let r=Math.max(0,e);typeof n.scrollTo==`function`?n.scrollTo({top:r,behavior:t??`auto`}):n.scrollTop=r}function t(t,n){let r=M.current;if(!r||u.length===0)return;let i=Math.max(0,Math.min(t,u.length-1)),a=H[i]??0,o=(H[i+1]??a)-a,s=N.current?.offsetHeight??0,c=Math.max(0,r.clientHeight-s),l=n?.align??`start`,d=a-s;l===`center`?d-=(c-o)/2:l===`end`&&(d-=c-o),e(d,n?.behavior)}return{scrollToIndex:t,scrollToKey(e,n){let r=u.findIndex((t,n)=>(f?f(t,n):n)===e);r>=0&&t(r,n)},scrollToTop(t){e(0,t?.behavior)},getScrollElement:()=>M.current}},[u,H,f]),{startIndex:K,endIndex:q}=o(()=>n(H,y,x,h,u.length),[H,y,x,h,u.length]);return{rootRef:M,headerRef:N,rowObserver:R,keysRef:L,offsets:H,totalHeight:U,containerHeight:x,containerWidth:C,startIndex:K,endIndex:q,handleScroll:V,showLoadMoreSpinner:k,scrollApi:G}}export{u as useVirtualWindow};
|
|
2
2
|
//# sourceMappingURL=useVirtualWindow.mjs.map
|