@react-aria/spinbutton 3.6.19 → 3.7.1
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/types.d.ts.map +1 -1
- package/dist/useSpinButton.main.js +106 -20
- package/dist/useSpinButton.main.js.map +1 -1
- package/dist/useSpinButton.mjs +107 -21
- package/dist/useSpinButton.module.js +107 -21
- package/dist/useSpinButton.module.js.map +1 -1
- package/package.json +6 -6
- package/src/useSpinButton.ts +123 -40
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;
|
|
1
|
+
{"mappings":";;AAwBA,gCAAiC,SAAQ,SAAS,EAAE,WAAW,MAAM,CAAC,EAAE,UAAU,MAAM,CAAC,EAAE,eAAe,MAAM,CAAC;IAC/G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED;IACE,eAAe,EAAE,aAAa,CAAC;IAC/B,oBAAoB,EAAE,eAAe,CAAC;IACtC,oBAAoB,EAAE,eAAe,CAAA;CACtC;AAED,8BACE,KAAK,EAAE,eAAe,GACrB,cAAc,CA+PhB","sources":["packages/@react-aria/spinbutton/src/packages/@react-aria/spinbutton/src/useSpinButton.ts","packages/@react-aria/spinbutton/src/packages/@react-aria/spinbutton/src/index.ts","packages/@react-aria/spinbutton/src/index.ts"],"sourcesContent":[null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nexport type {SpinButtonProps, SpinbuttonAria} from './useSpinButton';\nexport {useSpinButton} from './useSpinButton';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|
|
@@ -29,13 +29,21 @@ $parcel$export(module.exports, "useSpinButton", () => $37bbd4c129023f61$export$e
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
const $37bbd4c129023f61$var$noop = ()=>{};
|
|
32
33
|
function $37bbd4c129023f61$export$e908e06f4b8e3402(props) {
|
|
33
34
|
const _async = (0, $2pZbw$react.useRef)(undefined);
|
|
34
35
|
let { value: value, textValue: textValue, minValue: minValue, maxValue: maxValue, isDisabled: isDisabled, isReadOnly: isReadOnly, isRequired: isRequired, onIncrement: onIncrement, onIncrementPage: onIncrementPage, onDecrement: onDecrement, onDecrementPage: onDecrementPage, onDecrementToMin: onDecrementToMin, onIncrementToMax: onIncrementToMax } = props;
|
|
35
36
|
const stringFormatter = (0, $2pZbw$reactariai18n.useLocalizedStringFormatter)((0, ($parcel$interopDefault($cb4b786159079747$exports))), '@react-aria/spinbutton');
|
|
36
|
-
|
|
37
|
+
let isSpinning = (0, $2pZbw$react.useRef)(false);
|
|
38
|
+
const clearAsync = (0, $2pZbw$react.useCallback)(()=>{
|
|
39
|
+
clearTimeout(_async.current);
|
|
40
|
+
isSpinning.current = false;
|
|
41
|
+
}, []);
|
|
42
|
+
const clearAsyncEvent = (0, $2pZbw$reactariautils.useEffectEvent)(()=>{
|
|
43
|
+
clearAsync();
|
|
44
|
+
});
|
|
37
45
|
(0, $2pZbw$react.useEffect)(()=>{
|
|
38
|
-
return ()=>
|
|
46
|
+
return ()=>clearAsyncEvent();
|
|
39
47
|
}, []);
|
|
40
48
|
let onKeyDown = (e)=>{
|
|
41
49
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) return;
|
|
@@ -102,26 +110,61 @@ function $37bbd4c129023f61$export$e908e06f4b8e3402(props) {
|
|
|
102
110
|
}, [
|
|
103
111
|
ariaTextValue
|
|
104
112
|
]);
|
|
105
|
-
|
|
113
|
+
// For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.
|
|
114
|
+
let onPointerCancel = (0, $2pZbw$react.useCallback)(()=>{
|
|
106
115
|
clearAsync();
|
|
107
|
-
|
|
116
|
+
}, [
|
|
117
|
+
clearAsync
|
|
118
|
+
]);
|
|
119
|
+
const onIncrementEvent = (0, $2pZbw$reactariautils.useEffectEvent)(onIncrement !== null && onIncrement !== void 0 ? onIncrement : $37bbd4c129023f61$var$noop);
|
|
120
|
+
const onDecrementEvent = (0, $2pZbw$reactariautils.useEffectEvent)(onDecrement !== null && onDecrement !== void 0 ? onDecrement : $37bbd4c129023f61$var$noop);
|
|
121
|
+
const stepUpEvent = (0, $2pZbw$reactariautils.useEffectEvent)(()=>{
|
|
122
|
+
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {
|
|
123
|
+
onIncrementEvent();
|
|
124
|
+
onIncrementPressStartEvent(60);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const onIncrementPressStartEvent = (0, $2pZbw$reactariautils.useEffectEvent)((initialStepDelay)=>{
|
|
128
|
+
clearAsyncEvent();
|
|
129
|
+
isSpinning.current = true;
|
|
108
130
|
// Start spinning after initial delay
|
|
109
|
-
_async.current = window.setTimeout(
|
|
110
|
-
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) onIncrementPressStart(60);
|
|
111
|
-
}, initialStepDelay);
|
|
131
|
+
_async.current = window.setTimeout(stepUpEvent, initialStepDelay);
|
|
112
132
|
});
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
const stepDownEvent = (0, $2pZbw$reactariautils.useEffectEvent)(()=>{
|
|
134
|
+
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {
|
|
135
|
+
onDecrementEvent();
|
|
136
|
+
onDecrementPressStartEvent(60);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const onDecrementPressStartEvent = (0, $2pZbw$reactariautils.useEffectEvent)((initialStepDelay)=>{
|
|
140
|
+
clearAsyncEvent();
|
|
141
|
+
isSpinning.current = true;
|
|
116
142
|
// Start spinning after initial delay
|
|
117
|
-
_async.current = window.setTimeout(
|
|
118
|
-
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) onDecrementPressStart(60);
|
|
119
|
-
}, initialStepDelay);
|
|
143
|
+
_async.current = window.setTimeout(stepDownEvent, initialStepDelay);
|
|
120
144
|
});
|
|
121
145
|
let cancelContextMenu = (e)=>{
|
|
122
146
|
e.preventDefault();
|
|
123
147
|
};
|
|
124
148
|
let { addGlobalListener: addGlobalListener, removeAllGlobalListeners: removeAllGlobalListeners } = (0, $2pZbw$reactariautils.useGlobalListeners)();
|
|
149
|
+
// Tracks in touch if the press end event was preceded by a press up.
|
|
150
|
+
// If it wasn't, then we know the finger left the button while still in contact with the screen.
|
|
151
|
+
// This means that the user is trying to scroll or interact in some way that shouldn't trigger
|
|
152
|
+
// an increment or decrement.
|
|
153
|
+
let isUp = (0, $2pZbw$react.useRef)(false);
|
|
154
|
+
let [isIncrementPressed, setIsIncrementPressed] = (0, $2pZbw$react.useState)(null);
|
|
155
|
+
(0, $2pZbw$react.useEffect)(()=>{
|
|
156
|
+
if (isIncrementPressed === 'touch') onIncrementPressStartEvent(600);
|
|
157
|
+
else if (isIncrementPressed) onIncrementPressStartEvent(400);
|
|
158
|
+
}, [
|
|
159
|
+
isIncrementPressed
|
|
160
|
+
]);
|
|
161
|
+
let [isDecrementPressed, setIsDecrementPressed] = (0, $2pZbw$react.useState)(null);
|
|
162
|
+
(0, $2pZbw$react.useEffect)(()=>{
|
|
163
|
+
if (isDecrementPressed === 'touch') onDecrementPressStartEvent(600);
|
|
164
|
+
else if (isDecrementPressed) onDecrementPressStartEvent(400);
|
|
165
|
+
}, [
|
|
166
|
+
isDecrementPressed
|
|
167
|
+
]);
|
|
125
168
|
return {
|
|
126
169
|
spinButtonProps: {
|
|
127
170
|
role: 'spinbutton',
|
|
@@ -137,25 +180,68 @@ function $37bbd4c129023f61$export$e908e06f4b8e3402(props) {
|
|
|
137
180
|
onBlur: onBlur
|
|
138
181
|
},
|
|
139
182
|
incrementButtonProps: {
|
|
140
|
-
onPressStart: ()=>{
|
|
141
|
-
|
|
183
|
+
onPressStart: (e)=>{
|
|
184
|
+
clearAsync();
|
|
185
|
+
if (e.pointerType !== 'touch') {
|
|
186
|
+
onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
187
|
+
setIsIncrementPressed('mouse');
|
|
188
|
+
} else {
|
|
189
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
190
|
+
capture: true
|
|
191
|
+
});
|
|
192
|
+
isUp.current = false;
|
|
193
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
194
|
+
// the control isn't spinning.
|
|
195
|
+
setIsIncrementPressed('touch');
|
|
196
|
+
}
|
|
142
197
|
addGlobalListener(window, 'contextmenu', cancelContextMenu);
|
|
143
198
|
},
|
|
144
|
-
|
|
199
|
+
onPressUp: (e)=>{
|
|
145
200
|
clearAsync();
|
|
201
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
146
202
|
removeAllGlobalListeners();
|
|
203
|
+
setIsIncrementPressed(null);
|
|
204
|
+
},
|
|
205
|
+
onPressEnd: (e)=>{
|
|
206
|
+
clearAsync();
|
|
207
|
+
if (e.pointerType === 'touch') {
|
|
208
|
+
if (!isSpinning.current && isUp.current) onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
209
|
+
}
|
|
210
|
+
isUp.current = false;
|
|
211
|
+
setIsIncrementPressed(null);
|
|
147
212
|
},
|
|
148
213
|
onFocus: onFocus,
|
|
149
214
|
onBlur: onBlur
|
|
150
215
|
},
|
|
151
216
|
decrementButtonProps: {
|
|
152
|
-
onPressStart: ()=>{
|
|
153
|
-
|
|
154
|
-
|
|
217
|
+
onPressStart: (e)=>{
|
|
218
|
+
clearAsync();
|
|
219
|
+
if (e.pointerType !== 'touch') {
|
|
220
|
+
onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
221
|
+
setIsDecrementPressed('mouse');
|
|
222
|
+
} else {
|
|
223
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
224
|
+
capture: true
|
|
225
|
+
});
|
|
226
|
+
isUp.current = false;
|
|
227
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
228
|
+
// the control isn't spinning.
|
|
229
|
+
setIsDecrementPressed('touch');
|
|
230
|
+
}
|
|
155
231
|
},
|
|
156
|
-
|
|
232
|
+
onPressUp: (e)=>{
|
|
157
233
|
clearAsync();
|
|
234
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
158
235
|
removeAllGlobalListeners();
|
|
236
|
+
setIsDecrementPressed(null);
|
|
237
|
+
},
|
|
238
|
+
onPressEnd: (e)=>{
|
|
239
|
+
clearAsync();
|
|
240
|
+
if (e.pointerType === 'touch') {
|
|
241
|
+
if (!isSpinning.current && isUp.current) onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
242
|
+
}
|
|
243
|
+
isUp.current = false;
|
|
244
|
+
setIsDecrementPressed(null);
|
|
159
245
|
},
|
|
160
246
|
onFocus: onFocus,
|
|
161
247
|
onBlur: onBlur
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/@react-aria/spinbutton/src/useSpinButton.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce, clearAnnouncer} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {useEffect, useRef} from 'react';\nimport {useEffectEvent, useGlobalListeners} from '@react-aria/utils';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\n\n\nexport interface SpinButtonProps extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {\n textValue?: string,\n onIncrement?: () => void,\n onIncrementPage?: () => void,\n onDecrement?: () => void,\n onDecrementPage?: () => void,\n onDecrementToMin?: () => void,\n onIncrementToMax?: () => void\n}\n\nexport interface SpinbuttonAria {\n spinButtonProps: DOMAttributes,\n incrementButtonProps: AriaButtonProps,\n decrementButtonProps: AriaButtonProps\n}\n\nexport function useSpinButton(\n props: SpinButtonProps\n): SpinbuttonAria {\n const _async = useRef<number>(undefined);\n let {\n value,\n textValue,\n minValue,\n maxValue,\n isDisabled,\n isReadOnly,\n isRequired,\n onIncrement,\n onIncrementPage,\n onDecrement,\n onDecrementPage,\n onDecrementToMin,\n onIncrementToMax\n } = props;\n const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');\n\n const clearAsync = () => clearTimeout(_async.current);\n\n\n useEffect(() => {\n return () => clearAsync();\n }, []);\n\n let onKeyDown = (e) => {\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) {\n return;\n }\n\n switch (e.key) {\n case 'PageUp':\n if (onIncrementPage) {\n e.preventDefault();\n onIncrementPage?.();\n break;\n }\n // fallthrough!\n case 'ArrowUp':\n case 'Up':\n if (onIncrement) {\n e.preventDefault();\n onIncrement?.();\n }\n break;\n case 'PageDown':\n if (onDecrementPage) {\n e.preventDefault();\n onDecrementPage?.();\n break;\n }\n // fallthrough\n case 'ArrowDown':\n case 'Down':\n if (onDecrement) {\n e.preventDefault();\n onDecrement?.();\n }\n break;\n case 'Home':\n if (onDecrementToMin) {\n e.preventDefault();\n onDecrementToMin?.();\n }\n break;\n case 'End':\n if (onIncrementToMax) {\n e.preventDefault();\n onIncrementToMax?.();\n }\n break;\n }\n };\n\n let isFocused = useRef(false);\n let onFocus = () => {\n isFocused.current = true;\n };\n\n let onBlur = () => {\n isFocused.current = false;\n };\n\n // Replace Unicode hyphen-minus (U+002D) with minus sign (U+2212).\n // This ensures that macOS VoiceOver announces it as \"minus\" even with other characters between the minus sign\n // and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.\n // In addition, replace the empty string with the word \"Empty\" so that iOS VoiceOver does not read \"50%\" for an empty field.\n let ariaTextValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\\u2212');\n\n useEffect(() => {\n if (isFocused.current) {\n clearAnnouncer('assertive');\n announce(ariaTextValue, 'assertive');\n }\n }, [ariaTextValue]);\n\n const onIncrementPressStart = useEffectEvent(\n (initialStepDelay: number) => {\n clearAsync();\n onIncrement?.();\n // Start spinning after initial delay\n _async.current = window.setTimeout(\n () => {\n if ((maxValue === undefined || isNaN(maxValue)) || (value === undefined || isNaN(value)) || value < maxValue) {\n onIncrementPressStart(60);\n }\n },\n initialStepDelay\n );\n }\n );\n\n const onDecrementPressStart = useEffectEvent(\n (initialStepDelay: number) => {\n clearAsync();\n onDecrement?.();\n // Start spinning after initial delay\n _async.current = window.setTimeout(\n () => {\n if ((minValue === undefined || isNaN(minValue)) || (value === undefined || isNaN(value)) || value > minValue) {\n onDecrementPressStart(60);\n }\n },\n initialStepDelay\n );\n }\n );\n\n let cancelContextMenu = (e) => {\n e.preventDefault();\n };\n\n let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();\n\n return {\n spinButtonProps: {\n role: 'spinbutton',\n 'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,\n 'aria-valuetext': ariaTextValue,\n 'aria-valuemin': minValue,\n 'aria-valuemax': maxValue,\n 'aria-disabled': isDisabled || undefined,\n 'aria-readonly': isReadOnly || undefined,\n 'aria-required': isRequired || undefined,\n onKeyDown,\n onFocus,\n onBlur\n },\n incrementButtonProps: {\n onPressStart: () => {\n onIncrementPressStart(400);\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressEnd: () => {\n clearAsync();\n removeAllGlobalListeners();\n },\n onFocus,\n onBlur\n },\n decrementButtonProps: {\n onPressStart: () => {\n onDecrementPressStart(400);\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressEnd: () => {\n clearAsync();\n removeAllGlobalListeners();\n },\n onFocus,\n onBlur\n }\n };\n}\n"],"names":[],"version":3,"file":"useSpinButton.main.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/spinbutton/src/useSpinButton.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce, clearAnnouncer} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {useCallback, useEffect, useRef, useState} from 'react';\nimport {useEffectEvent, useGlobalListeners} from '@react-aria/utils';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\n\n\nconst noop = () => {};\n\nexport interface SpinButtonProps extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {\n textValue?: string,\n onIncrement?: () => void,\n onIncrementPage?: () => void,\n onDecrement?: () => void,\n onDecrementPage?: () => void,\n onDecrementToMin?: () => void,\n onIncrementToMax?: () => void\n}\n\nexport interface SpinbuttonAria {\n spinButtonProps: DOMAttributes,\n incrementButtonProps: AriaButtonProps,\n decrementButtonProps: AriaButtonProps\n}\n\nexport function useSpinButton(\n props: SpinButtonProps\n): SpinbuttonAria {\n const _async = useRef<number>(undefined);\n let {\n value,\n textValue,\n minValue,\n maxValue,\n isDisabled,\n isReadOnly,\n isRequired,\n onIncrement,\n onIncrementPage,\n onDecrement,\n onDecrementPage,\n onDecrementToMin,\n onIncrementToMax\n } = props;\n const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');\n\n let isSpinning = useRef(false);\n const clearAsync = useCallback(() => {\n clearTimeout(_async.current);\n isSpinning.current = false;\n }, []);\n const clearAsyncEvent = useEffectEvent(() => {\n clearAsync();\n });\n\n useEffect(() => {\n return () => clearAsyncEvent();\n }, []);\n\n let onKeyDown = (e) => {\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) {\n return;\n }\n\n switch (e.key) {\n case 'PageUp':\n if (onIncrementPage) {\n e.preventDefault();\n onIncrementPage?.();\n break;\n }\n // fallthrough!\n case 'ArrowUp':\n case 'Up':\n if (onIncrement) {\n e.preventDefault();\n onIncrement?.();\n }\n break;\n case 'PageDown':\n if (onDecrementPage) {\n e.preventDefault();\n onDecrementPage?.();\n break;\n }\n // fallthrough\n case 'ArrowDown':\n case 'Down':\n if (onDecrement) {\n e.preventDefault();\n onDecrement?.();\n }\n break;\n case 'Home':\n if (onDecrementToMin) {\n e.preventDefault();\n onDecrementToMin?.();\n }\n break;\n case 'End':\n if (onIncrementToMax) {\n e.preventDefault();\n onIncrementToMax?.();\n }\n break;\n }\n };\n\n let isFocused = useRef(false);\n let onFocus = () => {\n isFocused.current = true;\n };\n\n let onBlur = () => {\n isFocused.current = false;\n };\n\n // Replace Unicode hyphen-minus (U+002D) with minus sign (U+2212).\n // This ensures that macOS VoiceOver announces it as \"minus\" even with other characters between the minus sign\n // and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.\n // In addition, replace the empty string with the word \"Empty\" so that iOS VoiceOver does not read \"50%\" for an empty field.\n let ariaTextValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\\u2212');\n\n useEffect(() => {\n if (isFocused.current) {\n clearAnnouncer('assertive');\n announce(ariaTextValue, 'assertive');\n }\n }, [ariaTextValue]);\n\n // For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.\n let onPointerCancel = useCallback(() => {\n clearAsync();\n }, [clearAsync]);\n\n const onIncrementEvent = useEffectEvent(onIncrement ?? noop);\n const onDecrementEvent = useEffectEvent(onDecrement ?? noop);\n\n const stepUpEvent = useEffectEvent(() => {\n if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {\n onIncrementEvent();\n onIncrementPressStartEvent(60);\n }\n });\n\n const onIncrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {\n clearAsyncEvent();\n isSpinning.current = true;\n // Start spinning after initial delay\n _async.current = window.setTimeout(stepUpEvent, initialStepDelay);\n });\n\n const stepDownEvent = useEffectEvent(() => {\n if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {\n onDecrementEvent();\n onDecrementPressStartEvent(60);\n }\n });\n\n const onDecrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {\n clearAsyncEvent();\n isSpinning.current = true;\n // Start spinning after initial delay\n _async.current = window.setTimeout(stepDownEvent, initialStepDelay);\n });\n\n let cancelContextMenu = (e) => {\n e.preventDefault();\n };\n\n let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();\n\n // Tracks in touch if the press end event was preceded by a press up.\n // If it wasn't, then we know the finger left the button while still in contact with the screen.\n // This means that the user is trying to scroll or interact in some way that shouldn't trigger\n // an increment or decrement.\n let isUp = useRef(false);\n\n let [isIncrementPressed, setIsIncrementPressed] = useState<'touch' | 'mouse' | null>(null);\n useEffect(() => {\n if (isIncrementPressed === 'touch') {\n onIncrementPressStartEvent(600);\n } else if (isIncrementPressed) {\n onIncrementPressStartEvent(400);\n }\n }, [isIncrementPressed]);\n\n let [isDecrementPressed, setIsDecrementPressed] = useState<'touch' | 'mouse' | null>(null);\n useEffect(() => {\n if (isDecrementPressed === 'touch') {\n onDecrementPressStartEvent(600);\n } else if (isDecrementPressed) {\n onDecrementPressStartEvent(400);\n }\n }, [isDecrementPressed]);\n\n return {\n spinButtonProps: {\n role: 'spinbutton',\n 'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,\n 'aria-valuetext': ariaTextValue,\n 'aria-valuemin': minValue,\n 'aria-valuemax': maxValue,\n 'aria-disabled': isDisabled || undefined,\n 'aria-readonly': isReadOnly || undefined,\n 'aria-required': isRequired || undefined,\n onKeyDown,\n onFocus,\n onBlur\n },\n incrementButtonProps: {\n onPressStart: (e) => {\n clearAsync();\n if (e.pointerType !== 'touch') {\n onIncrement?.();\n setIsIncrementPressed('mouse');\n } else {\n addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});\n isUp.current = false;\n // For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if\n // the control isn't spinning.\n setIsIncrementPressed('touch');\n }\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressUp: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n isUp.current = true;\n }\n removeAllGlobalListeners();\n setIsIncrementPressed(null);\n },\n onPressEnd: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n if (!isSpinning.current && isUp.current) {\n onIncrement?.();\n }\n }\n isUp.current = false;\n setIsIncrementPressed(null);\n },\n onFocus,\n onBlur\n },\n decrementButtonProps: {\n onPressStart: (e) => {\n clearAsync();\n if (e.pointerType !== 'touch') {\n onDecrement?.();\n setIsDecrementPressed('mouse');\n } else {\n addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});\n isUp.current = false;\n // For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if\n // the control isn't spinning.\n setIsDecrementPressed('touch');\n }\n },\n onPressUp: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n isUp.current = true;\n }\n removeAllGlobalListeners();\n setIsDecrementPressed(null);\n },\n onPressEnd: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n if (!isSpinning.current && isUp.current) {\n onDecrement?.();\n }\n }\n isUp.current = false;\n setIsDecrementPressed(null);\n },\n onFocus,\n onBlur\n }\n };\n}\n"],"names":[],"version":3,"file":"useSpinButton.main.js.map"}
|
package/dist/useSpinButton.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import $5rwhf$intlStringsmodulejs from "./intlStrings.mjs";
|
|
2
2
|
import {clearAnnouncer as $5rwhf$clearAnnouncer, announce as $5rwhf$announce} from "@react-aria/live-announcer";
|
|
3
|
-
import {useRef as $5rwhf$useRef, useEffect as $5rwhf$useEffect} from "react";
|
|
3
|
+
import {useRef as $5rwhf$useRef, useCallback as $5rwhf$useCallback, useEffect as $5rwhf$useEffect, useState as $5rwhf$useState} from "react";
|
|
4
4
|
import {useEffectEvent as $5rwhf$useEffectEvent, useGlobalListeners as $5rwhf$useGlobalListeners} from "@react-aria/utils";
|
|
5
5
|
import {useLocalizedStringFormatter as $5rwhf$useLocalizedStringFormatter} from "@react-aria/i18n";
|
|
6
6
|
|
|
@@ -23,13 +23,21 @@ function $parcel$interopDefault(a) {
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
const $d2e8511e6f209edf$var$noop = ()=>{};
|
|
26
27
|
function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
27
28
|
const _async = (0, $5rwhf$useRef)(undefined);
|
|
28
29
|
let { value: value, textValue: textValue, minValue: minValue, maxValue: maxValue, isDisabled: isDisabled, isReadOnly: isReadOnly, isRequired: isRequired, onIncrement: onIncrement, onIncrementPage: onIncrementPage, onDecrement: onDecrement, onDecrementPage: onDecrementPage, onDecrementToMin: onDecrementToMin, onIncrementToMax: onIncrementToMax } = props;
|
|
29
30
|
const stringFormatter = (0, $5rwhf$useLocalizedStringFormatter)((0, ($parcel$interopDefault($5rwhf$intlStringsmodulejs))), '@react-aria/spinbutton');
|
|
30
|
-
|
|
31
|
+
let isSpinning = (0, $5rwhf$useRef)(false);
|
|
32
|
+
const clearAsync = (0, $5rwhf$useCallback)(()=>{
|
|
33
|
+
clearTimeout(_async.current);
|
|
34
|
+
isSpinning.current = false;
|
|
35
|
+
}, []);
|
|
36
|
+
const clearAsyncEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
37
|
+
clearAsync();
|
|
38
|
+
});
|
|
31
39
|
(0, $5rwhf$useEffect)(()=>{
|
|
32
|
-
return ()=>
|
|
40
|
+
return ()=>clearAsyncEvent();
|
|
33
41
|
}, []);
|
|
34
42
|
let onKeyDown = (e)=>{
|
|
35
43
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) return;
|
|
@@ -96,26 +104,61 @@ function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
|
96
104
|
}, [
|
|
97
105
|
ariaTextValue
|
|
98
106
|
]);
|
|
99
|
-
|
|
107
|
+
// For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.
|
|
108
|
+
let onPointerCancel = (0, $5rwhf$useCallback)(()=>{
|
|
100
109
|
clearAsync();
|
|
101
|
-
|
|
110
|
+
}, [
|
|
111
|
+
clearAsync
|
|
112
|
+
]);
|
|
113
|
+
const onIncrementEvent = (0, $5rwhf$useEffectEvent)(onIncrement !== null && onIncrement !== void 0 ? onIncrement : $d2e8511e6f209edf$var$noop);
|
|
114
|
+
const onDecrementEvent = (0, $5rwhf$useEffectEvent)(onDecrement !== null && onDecrement !== void 0 ? onDecrement : $d2e8511e6f209edf$var$noop);
|
|
115
|
+
const stepUpEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
116
|
+
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {
|
|
117
|
+
onIncrementEvent();
|
|
118
|
+
onIncrementPressStartEvent(60);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const onIncrementPressStartEvent = (0, $5rwhf$useEffectEvent)((initialStepDelay)=>{
|
|
122
|
+
clearAsyncEvent();
|
|
123
|
+
isSpinning.current = true;
|
|
102
124
|
// Start spinning after initial delay
|
|
103
|
-
_async.current = window.setTimeout(
|
|
104
|
-
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) onIncrementPressStart(60);
|
|
105
|
-
}, initialStepDelay);
|
|
125
|
+
_async.current = window.setTimeout(stepUpEvent, initialStepDelay);
|
|
106
126
|
});
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
const stepDownEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
128
|
+
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {
|
|
129
|
+
onDecrementEvent();
|
|
130
|
+
onDecrementPressStartEvent(60);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
const onDecrementPressStartEvent = (0, $5rwhf$useEffectEvent)((initialStepDelay)=>{
|
|
134
|
+
clearAsyncEvent();
|
|
135
|
+
isSpinning.current = true;
|
|
110
136
|
// Start spinning after initial delay
|
|
111
|
-
_async.current = window.setTimeout(
|
|
112
|
-
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) onDecrementPressStart(60);
|
|
113
|
-
}, initialStepDelay);
|
|
137
|
+
_async.current = window.setTimeout(stepDownEvent, initialStepDelay);
|
|
114
138
|
});
|
|
115
139
|
let cancelContextMenu = (e)=>{
|
|
116
140
|
e.preventDefault();
|
|
117
141
|
};
|
|
118
142
|
let { addGlobalListener: addGlobalListener, removeAllGlobalListeners: removeAllGlobalListeners } = (0, $5rwhf$useGlobalListeners)();
|
|
143
|
+
// Tracks in touch if the press end event was preceded by a press up.
|
|
144
|
+
// If it wasn't, then we know the finger left the button while still in contact with the screen.
|
|
145
|
+
// This means that the user is trying to scroll or interact in some way that shouldn't trigger
|
|
146
|
+
// an increment or decrement.
|
|
147
|
+
let isUp = (0, $5rwhf$useRef)(false);
|
|
148
|
+
let [isIncrementPressed, setIsIncrementPressed] = (0, $5rwhf$useState)(null);
|
|
149
|
+
(0, $5rwhf$useEffect)(()=>{
|
|
150
|
+
if (isIncrementPressed === 'touch') onIncrementPressStartEvent(600);
|
|
151
|
+
else if (isIncrementPressed) onIncrementPressStartEvent(400);
|
|
152
|
+
}, [
|
|
153
|
+
isIncrementPressed
|
|
154
|
+
]);
|
|
155
|
+
let [isDecrementPressed, setIsDecrementPressed] = (0, $5rwhf$useState)(null);
|
|
156
|
+
(0, $5rwhf$useEffect)(()=>{
|
|
157
|
+
if (isDecrementPressed === 'touch') onDecrementPressStartEvent(600);
|
|
158
|
+
else if (isDecrementPressed) onDecrementPressStartEvent(400);
|
|
159
|
+
}, [
|
|
160
|
+
isDecrementPressed
|
|
161
|
+
]);
|
|
119
162
|
return {
|
|
120
163
|
spinButtonProps: {
|
|
121
164
|
role: 'spinbutton',
|
|
@@ -131,25 +174,68 @@ function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
|
131
174
|
onBlur: onBlur
|
|
132
175
|
},
|
|
133
176
|
incrementButtonProps: {
|
|
134
|
-
onPressStart: ()=>{
|
|
135
|
-
|
|
177
|
+
onPressStart: (e)=>{
|
|
178
|
+
clearAsync();
|
|
179
|
+
if (e.pointerType !== 'touch') {
|
|
180
|
+
onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
181
|
+
setIsIncrementPressed('mouse');
|
|
182
|
+
} else {
|
|
183
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
184
|
+
capture: true
|
|
185
|
+
});
|
|
186
|
+
isUp.current = false;
|
|
187
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
188
|
+
// the control isn't spinning.
|
|
189
|
+
setIsIncrementPressed('touch');
|
|
190
|
+
}
|
|
136
191
|
addGlobalListener(window, 'contextmenu', cancelContextMenu);
|
|
137
192
|
},
|
|
138
|
-
|
|
193
|
+
onPressUp: (e)=>{
|
|
139
194
|
clearAsync();
|
|
195
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
140
196
|
removeAllGlobalListeners();
|
|
197
|
+
setIsIncrementPressed(null);
|
|
198
|
+
},
|
|
199
|
+
onPressEnd: (e)=>{
|
|
200
|
+
clearAsync();
|
|
201
|
+
if (e.pointerType === 'touch') {
|
|
202
|
+
if (!isSpinning.current && isUp.current) onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
203
|
+
}
|
|
204
|
+
isUp.current = false;
|
|
205
|
+
setIsIncrementPressed(null);
|
|
141
206
|
},
|
|
142
207
|
onFocus: onFocus,
|
|
143
208
|
onBlur: onBlur
|
|
144
209
|
},
|
|
145
210
|
decrementButtonProps: {
|
|
146
|
-
onPressStart: ()=>{
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
onPressStart: (e)=>{
|
|
212
|
+
clearAsync();
|
|
213
|
+
if (e.pointerType !== 'touch') {
|
|
214
|
+
onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
215
|
+
setIsDecrementPressed('mouse');
|
|
216
|
+
} else {
|
|
217
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
218
|
+
capture: true
|
|
219
|
+
});
|
|
220
|
+
isUp.current = false;
|
|
221
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
222
|
+
// the control isn't spinning.
|
|
223
|
+
setIsDecrementPressed('touch');
|
|
224
|
+
}
|
|
149
225
|
},
|
|
150
|
-
|
|
226
|
+
onPressUp: (e)=>{
|
|
151
227
|
clearAsync();
|
|
228
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
152
229
|
removeAllGlobalListeners();
|
|
230
|
+
setIsDecrementPressed(null);
|
|
231
|
+
},
|
|
232
|
+
onPressEnd: (e)=>{
|
|
233
|
+
clearAsync();
|
|
234
|
+
if (e.pointerType === 'touch') {
|
|
235
|
+
if (!isSpinning.current && isUp.current) onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
236
|
+
}
|
|
237
|
+
isUp.current = false;
|
|
238
|
+
setIsDecrementPressed(null);
|
|
153
239
|
},
|
|
154
240
|
onFocus: onFocus,
|
|
155
241
|
onBlur: onBlur
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import $5rwhf$intlStringsmodulejs from "./intlStrings.module.js";
|
|
2
2
|
import {clearAnnouncer as $5rwhf$clearAnnouncer, announce as $5rwhf$announce} from "@react-aria/live-announcer";
|
|
3
|
-
import {useRef as $5rwhf$useRef, useEffect as $5rwhf$useEffect} from "react";
|
|
3
|
+
import {useRef as $5rwhf$useRef, useCallback as $5rwhf$useCallback, useEffect as $5rwhf$useEffect, useState as $5rwhf$useState} from "react";
|
|
4
4
|
import {useEffectEvent as $5rwhf$useEffectEvent, useGlobalListeners as $5rwhf$useGlobalListeners} from "@react-aria/utils";
|
|
5
5
|
import {useLocalizedStringFormatter as $5rwhf$useLocalizedStringFormatter} from "@react-aria/i18n";
|
|
6
6
|
|
|
@@ -23,13 +23,21 @@ function $parcel$interopDefault(a) {
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
const $d2e8511e6f209edf$var$noop = ()=>{};
|
|
26
27
|
function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
27
28
|
const _async = (0, $5rwhf$useRef)(undefined);
|
|
28
29
|
let { value: value, textValue: textValue, minValue: minValue, maxValue: maxValue, isDisabled: isDisabled, isReadOnly: isReadOnly, isRequired: isRequired, onIncrement: onIncrement, onIncrementPage: onIncrementPage, onDecrement: onDecrement, onDecrementPage: onDecrementPage, onDecrementToMin: onDecrementToMin, onIncrementToMax: onIncrementToMax } = props;
|
|
29
30
|
const stringFormatter = (0, $5rwhf$useLocalizedStringFormatter)((0, ($parcel$interopDefault($5rwhf$intlStringsmodulejs))), '@react-aria/spinbutton');
|
|
30
|
-
|
|
31
|
+
let isSpinning = (0, $5rwhf$useRef)(false);
|
|
32
|
+
const clearAsync = (0, $5rwhf$useCallback)(()=>{
|
|
33
|
+
clearTimeout(_async.current);
|
|
34
|
+
isSpinning.current = false;
|
|
35
|
+
}, []);
|
|
36
|
+
const clearAsyncEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
37
|
+
clearAsync();
|
|
38
|
+
});
|
|
31
39
|
(0, $5rwhf$useEffect)(()=>{
|
|
32
|
-
return ()=>
|
|
40
|
+
return ()=>clearAsyncEvent();
|
|
33
41
|
}, []);
|
|
34
42
|
let onKeyDown = (e)=>{
|
|
35
43
|
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) return;
|
|
@@ -96,26 +104,61 @@ function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
|
96
104
|
}, [
|
|
97
105
|
ariaTextValue
|
|
98
106
|
]);
|
|
99
|
-
|
|
107
|
+
// For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.
|
|
108
|
+
let onPointerCancel = (0, $5rwhf$useCallback)(()=>{
|
|
100
109
|
clearAsync();
|
|
101
|
-
|
|
110
|
+
}, [
|
|
111
|
+
clearAsync
|
|
112
|
+
]);
|
|
113
|
+
const onIncrementEvent = (0, $5rwhf$useEffectEvent)(onIncrement !== null && onIncrement !== void 0 ? onIncrement : $d2e8511e6f209edf$var$noop);
|
|
114
|
+
const onDecrementEvent = (0, $5rwhf$useEffectEvent)(onDecrement !== null && onDecrement !== void 0 ? onDecrement : $d2e8511e6f209edf$var$noop);
|
|
115
|
+
const stepUpEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
116
|
+
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {
|
|
117
|
+
onIncrementEvent();
|
|
118
|
+
onIncrementPressStartEvent(60);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const onIncrementPressStartEvent = (0, $5rwhf$useEffectEvent)((initialStepDelay)=>{
|
|
122
|
+
clearAsyncEvent();
|
|
123
|
+
isSpinning.current = true;
|
|
102
124
|
// Start spinning after initial delay
|
|
103
|
-
_async.current = window.setTimeout(
|
|
104
|
-
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) onIncrementPressStart(60);
|
|
105
|
-
}, initialStepDelay);
|
|
125
|
+
_async.current = window.setTimeout(stepUpEvent, initialStepDelay);
|
|
106
126
|
});
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
const stepDownEvent = (0, $5rwhf$useEffectEvent)(()=>{
|
|
128
|
+
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {
|
|
129
|
+
onDecrementEvent();
|
|
130
|
+
onDecrementPressStartEvent(60);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
const onDecrementPressStartEvent = (0, $5rwhf$useEffectEvent)((initialStepDelay)=>{
|
|
134
|
+
clearAsyncEvent();
|
|
135
|
+
isSpinning.current = true;
|
|
110
136
|
// Start spinning after initial delay
|
|
111
|
-
_async.current = window.setTimeout(
|
|
112
|
-
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) onDecrementPressStart(60);
|
|
113
|
-
}, initialStepDelay);
|
|
137
|
+
_async.current = window.setTimeout(stepDownEvent, initialStepDelay);
|
|
114
138
|
});
|
|
115
139
|
let cancelContextMenu = (e)=>{
|
|
116
140
|
e.preventDefault();
|
|
117
141
|
};
|
|
118
142
|
let { addGlobalListener: addGlobalListener, removeAllGlobalListeners: removeAllGlobalListeners } = (0, $5rwhf$useGlobalListeners)();
|
|
143
|
+
// Tracks in touch if the press end event was preceded by a press up.
|
|
144
|
+
// If it wasn't, then we know the finger left the button while still in contact with the screen.
|
|
145
|
+
// This means that the user is trying to scroll or interact in some way that shouldn't trigger
|
|
146
|
+
// an increment or decrement.
|
|
147
|
+
let isUp = (0, $5rwhf$useRef)(false);
|
|
148
|
+
let [isIncrementPressed, setIsIncrementPressed] = (0, $5rwhf$useState)(null);
|
|
149
|
+
(0, $5rwhf$useEffect)(()=>{
|
|
150
|
+
if (isIncrementPressed === 'touch') onIncrementPressStartEvent(600);
|
|
151
|
+
else if (isIncrementPressed) onIncrementPressStartEvent(400);
|
|
152
|
+
}, [
|
|
153
|
+
isIncrementPressed
|
|
154
|
+
]);
|
|
155
|
+
let [isDecrementPressed, setIsDecrementPressed] = (0, $5rwhf$useState)(null);
|
|
156
|
+
(0, $5rwhf$useEffect)(()=>{
|
|
157
|
+
if (isDecrementPressed === 'touch') onDecrementPressStartEvent(600);
|
|
158
|
+
else if (isDecrementPressed) onDecrementPressStartEvent(400);
|
|
159
|
+
}, [
|
|
160
|
+
isDecrementPressed
|
|
161
|
+
]);
|
|
119
162
|
return {
|
|
120
163
|
spinButtonProps: {
|
|
121
164
|
role: 'spinbutton',
|
|
@@ -131,25 +174,68 @@ function $d2e8511e6f209edf$export$e908e06f4b8e3402(props) {
|
|
|
131
174
|
onBlur: onBlur
|
|
132
175
|
},
|
|
133
176
|
incrementButtonProps: {
|
|
134
|
-
onPressStart: ()=>{
|
|
135
|
-
|
|
177
|
+
onPressStart: (e)=>{
|
|
178
|
+
clearAsync();
|
|
179
|
+
if (e.pointerType !== 'touch') {
|
|
180
|
+
onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
181
|
+
setIsIncrementPressed('mouse');
|
|
182
|
+
} else {
|
|
183
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
184
|
+
capture: true
|
|
185
|
+
});
|
|
186
|
+
isUp.current = false;
|
|
187
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
188
|
+
// the control isn't spinning.
|
|
189
|
+
setIsIncrementPressed('touch');
|
|
190
|
+
}
|
|
136
191
|
addGlobalListener(window, 'contextmenu', cancelContextMenu);
|
|
137
192
|
},
|
|
138
|
-
|
|
193
|
+
onPressUp: (e)=>{
|
|
139
194
|
clearAsync();
|
|
195
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
140
196
|
removeAllGlobalListeners();
|
|
197
|
+
setIsIncrementPressed(null);
|
|
198
|
+
},
|
|
199
|
+
onPressEnd: (e)=>{
|
|
200
|
+
clearAsync();
|
|
201
|
+
if (e.pointerType === 'touch') {
|
|
202
|
+
if (!isSpinning.current && isUp.current) onIncrement === null || onIncrement === void 0 ? void 0 : onIncrement();
|
|
203
|
+
}
|
|
204
|
+
isUp.current = false;
|
|
205
|
+
setIsIncrementPressed(null);
|
|
141
206
|
},
|
|
142
207
|
onFocus: onFocus,
|
|
143
208
|
onBlur: onBlur
|
|
144
209
|
},
|
|
145
210
|
decrementButtonProps: {
|
|
146
|
-
onPressStart: ()=>{
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
onPressStart: (e)=>{
|
|
212
|
+
clearAsync();
|
|
213
|
+
if (e.pointerType !== 'touch') {
|
|
214
|
+
onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
215
|
+
setIsDecrementPressed('mouse');
|
|
216
|
+
} else {
|
|
217
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {
|
|
218
|
+
capture: true
|
|
219
|
+
});
|
|
220
|
+
isUp.current = false;
|
|
221
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
222
|
+
// the control isn't spinning.
|
|
223
|
+
setIsDecrementPressed('touch');
|
|
224
|
+
}
|
|
149
225
|
},
|
|
150
|
-
|
|
226
|
+
onPressUp: (e)=>{
|
|
151
227
|
clearAsync();
|
|
228
|
+
if (e.pointerType === 'touch') isUp.current = true;
|
|
152
229
|
removeAllGlobalListeners();
|
|
230
|
+
setIsDecrementPressed(null);
|
|
231
|
+
},
|
|
232
|
+
onPressEnd: (e)=>{
|
|
233
|
+
clearAsync();
|
|
234
|
+
if (e.pointerType === 'touch') {
|
|
235
|
+
if (!isSpinning.current && isUp.current) onDecrement === null || onDecrement === void 0 ? void 0 : onDecrement();
|
|
236
|
+
}
|
|
237
|
+
isUp.current = false;
|
|
238
|
+
setIsDecrementPressed(null);
|
|
153
239
|
},
|
|
154
240
|
onFocus: onFocus,
|
|
155
241
|
onBlur: onBlur
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/@react-aria/spinbutton/src/useSpinButton.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce, clearAnnouncer} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {useEffect, useRef} from 'react';\nimport {useEffectEvent, useGlobalListeners} from '@react-aria/utils';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\n\n\nexport interface SpinButtonProps extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {\n textValue?: string,\n onIncrement?: () => void,\n onIncrementPage?: () => void,\n onDecrement?: () => void,\n onDecrementPage?: () => void,\n onDecrementToMin?: () => void,\n onIncrementToMax?: () => void\n}\n\nexport interface SpinbuttonAria {\n spinButtonProps: DOMAttributes,\n incrementButtonProps: AriaButtonProps,\n decrementButtonProps: AriaButtonProps\n}\n\nexport function useSpinButton(\n props: SpinButtonProps\n): SpinbuttonAria {\n const _async = useRef<number>(undefined);\n let {\n value,\n textValue,\n minValue,\n maxValue,\n isDisabled,\n isReadOnly,\n isRequired,\n onIncrement,\n onIncrementPage,\n onDecrement,\n onDecrementPage,\n onDecrementToMin,\n onIncrementToMax\n } = props;\n const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');\n\n const clearAsync = () => clearTimeout(_async.current);\n\n\n useEffect(() => {\n return () => clearAsync();\n }, []);\n\n let onKeyDown = (e) => {\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) {\n return;\n }\n\n switch (e.key) {\n case 'PageUp':\n if (onIncrementPage) {\n e.preventDefault();\n onIncrementPage?.();\n break;\n }\n // fallthrough!\n case 'ArrowUp':\n case 'Up':\n if (onIncrement) {\n e.preventDefault();\n onIncrement?.();\n }\n break;\n case 'PageDown':\n if (onDecrementPage) {\n e.preventDefault();\n onDecrementPage?.();\n break;\n }\n // fallthrough\n case 'ArrowDown':\n case 'Down':\n if (onDecrement) {\n e.preventDefault();\n onDecrement?.();\n }\n break;\n case 'Home':\n if (onDecrementToMin) {\n e.preventDefault();\n onDecrementToMin?.();\n }\n break;\n case 'End':\n if (onIncrementToMax) {\n e.preventDefault();\n onIncrementToMax?.();\n }\n break;\n }\n };\n\n let isFocused = useRef(false);\n let onFocus = () => {\n isFocused.current = true;\n };\n\n let onBlur = () => {\n isFocused.current = false;\n };\n\n // Replace Unicode hyphen-minus (U+002D) with minus sign (U+2212).\n // This ensures that macOS VoiceOver announces it as \"minus\" even with other characters between the minus sign\n // and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.\n // In addition, replace the empty string with the word \"Empty\" so that iOS VoiceOver does not read \"50%\" for an empty field.\n let ariaTextValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\\u2212');\n\n useEffect(() => {\n if (isFocused.current) {\n clearAnnouncer('assertive');\n announce(ariaTextValue, 'assertive');\n }\n }, [ariaTextValue]);\n\n const onIncrementPressStart = useEffectEvent(\n (initialStepDelay: number) => {\n clearAsync();\n onIncrement?.();\n // Start spinning after initial delay\n _async.current = window.setTimeout(\n () => {\n if ((maxValue === undefined || isNaN(maxValue)) || (value === undefined || isNaN(value)) || value < maxValue) {\n onIncrementPressStart(60);\n }\n },\n initialStepDelay\n );\n }\n );\n\n const onDecrementPressStart = useEffectEvent(\n (initialStepDelay: number) => {\n clearAsync();\n onDecrement?.();\n // Start spinning after initial delay\n _async.current = window.setTimeout(\n () => {\n if ((minValue === undefined || isNaN(minValue)) || (value === undefined || isNaN(value)) || value > minValue) {\n onDecrementPressStart(60);\n }\n },\n initialStepDelay\n );\n }\n );\n\n let cancelContextMenu = (e) => {\n e.preventDefault();\n };\n\n let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();\n\n return {\n spinButtonProps: {\n role: 'spinbutton',\n 'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,\n 'aria-valuetext': ariaTextValue,\n 'aria-valuemin': minValue,\n 'aria-valuemax': maxValue,\n 'aria-disabled': isDisabled || undefined,\n 'aria-readonly': isReadOnly || undefined,\n 'aria-required': isRequired || undefined,\n onKeyDown,\n onFocus,\n onBlur\n },\n incrementButtonProps: {\n onPressStart: () => {\n onIncrementPressStart(400);\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressEnd: () => {\n clearAsync();\n removeAllGlobalListeners();\n },\n onFocus,\n onBlur\n },\n decrementButtonProps: {\n onPressStart: () => {\n onDecrementPressStart(400);\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressEnd: () => {\n clearAsync();\n removeAllGlobalListeners();\n },\n onFocus,\n onBlur\n }\n };\n}\n"],"names":[],"version":3,"file":"useSpinButton.module.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/spinbutton/src/useSpinButton.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce, clearAnnouncer} from '@react-aria/live-announcer';\nimport {AriaButtonProps} from '@react-types/button';\nimport {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {useCallback, useEffect, useRef, useState} from 'react';\nimport {useEffectEvent, useGlobalListeners} from '@react-aria/utils';\nimport {useLocalizedStringFormatter} from '@react-aria/i18n';\n\n\nconst noop = () => {};\n\nexport interface SpinButtonProps extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {\n textValue?: string,\n onIncrement?: () => void,\n onIncrementPage?: () => void,\n onDecrement?: () => void,\n onDecrementPage?: () => void,\n onDecrementToMin?: () => void,\n onIncrementToMax?: () => void\n}\n\nexport interface SpinbuttonAria {\n spinButtonProps: DOMAttributes,\n incrementButtonProps: AriaButtonProps,\n decrementButtonProps: AriaButtonProps\n}\n\nexport function useSpinButton(\n props: SpinButtonProps\n): SpinbuttonAria {\n const _async = useRef<number>(undefined);\n let {\n value,\n textValue,\n minValue,\n maxValue,\n isDisabled,\n isReadOnly,\n isRequired,\n onIncrement,\n onIncrementPage,\n onDecrement,\n onDecrementPage,\n onDecrementToMin,\n onIncrementToMax\n } = props;\n const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');\n\n let isSpinning = useRef(false);\n const clearAsync = useCallback(() => {\n clearTimeout(_async.current);\n isSpinning.current = false;\n }, []);\n const clearAsyncEvent = useEffectEvent(() => {\n clearAsync();\n });\n\n useEffect(() => {\n return () => clearAsyncEvent();\n }, []);\n\n let onKeyDown = (e) => {\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || isReadOnly || e.nativeEvent.isComposing) {\n return;\n }\n\n switch (e.key) {\n case 'PageUp':\n if (onIncrementPage) {\n e.preventDefault();\n onIncrementPage?.();\n break;\n }\n // fallthrough!\n case 'ArrowUp':\n case 'Up':\n if (onIncrement) {\n e.preventDefault();\n onIncrement?.();\n }\n break;\n case 'PageDown':\n if (onDecrementPage) {\n e.preventDefault();\n onDecrementPage?.();\n break;\n }\n // fallthrough\n case 'ArrowDown':\n case 'Down':\n if (onDecrement) {\n e.preventDefault();\n onDecrement?.();\n }\n break;\n case 'Home':\n if (onDecrementToMin) {\n e.preventDefault();\n onDecrementToMin?.();\n }\n break;\n case 'End':\n if (onIncrementToMax) {\n e.preventDefault();\n onIncrementToMax?.();\n }\n break;\n }\n };\n\n let isFocused = useRef(false);\n let onFocus = () => {\n isFocused.current = true;\n };\n\n let onBlur = () => {\n isFocused.current = false;\n };\n\n // Replace Unicode hyphen-minus (U+002D) with minus sign (U+2212).\n // This ensures that macOS VoiceOver announces it as \"minus\" even with other characters between the minus sign\n // and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.\n // In addition, replace the empty string with the word \"Empty\" so that iOS VoiceOver does not read \"50%\" for an empty field.\n let ariaTextValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\\u2212');\n\n useEffect(() => {\n if (isFocused.current) {\n clearAnnouncer('assertive');\n announce(ariaTextValue, 'assertive');\n }\n }, [ariaTextValue]);\n\n // For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.\n let onPointerCancel = useCallback(() => {\n clearAsync();\n }, [clearAsync]);\n\n const onIncrementEvent = useEffectEvent(onIncrement ?? noop);\n const onDecrementEvent = useEffectEvent(onDecrement ?? noop);\n\n const stepUpEvent = useEffectEvent(() => {\n if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {\n onIncrementEvent();\n onIncrementPressStartEvent(60);\n }\n });\n\n const onIncrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {\n clearAsyncEvent();\n isSpinning.current = true;\n // Start spinning after initial delay\n _async.current = window.setTimeout(stepUpEvent, initialStepDelay);\n });\n\n const stepDownEvent = useEffectEvent(() => {\n if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {\n onDecrementEvent();\n onDecrementPressStartEvent(60);\n }\n });\n\n const onDecrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {\n clearAsyncEvent();\n isSpinning.current = true;\n // Start spinning after initial delay\n _async.current = window.setTimeout(stepDownEvent, initialStepDelay);\n });\n\n let cancelContextMenu = (e) => {\n e.preventDefault();\n };\n\n let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();\n\n // Tracks in touch if the press end event was preceded by a press up.\n // If it wasn't, then we know the finger left the button while still in contact with the screen.\n // This means that the user is trying to scroll or interact in some way that shouldn't trigger\n // an increment or decrement.\n let isUp = useRef(false);\n\n let [isIncrementPressed, setIsIncrementPressed] = useState<'touch' | 'mouse' | null>(null);\n useEffect(() => {\n if (isIncrementPressed === 'touch') {\n onIncrementPressStartEvent(600);\n } else if (isIncrementPressed) {\n onIncrementPressStartEvent(400);\n }\n }, [isIncrementPressed]);\n\n let [isDecrementPressed, setIsDecrementPressed] = useState<'touch' | 'mouse' | null>(null);\n useEffect(() => {\n if (isDecrementPressed === 'touch') {\n onDecrementPressStartEvent(600);\n } else if (isDecrementPressed) {\n onDecrementPressStartEvent(400);\n }\n }, [isDecrementPressed]);\n\n return {\n spinButtonProps: {\n role: 'spinbutton',\n 'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,\n 'aria-valuetext': ariaTextValue,\n 'aria-valuemin': minValue,\n 'aria-valuemax': maxValue,\n 'aria-disabled': isDisabled || undefined,\n 'aria-readonly': isReadOnly || undefined,\n 'aria-required': isRequired || undefined,\n onKeyDown,\n onFocus,\n onBlur\n },\n incrementButtonProps: {\n onPressStart: (e) => {\n clearAsync();\n if (e.pointerType !== 'touch') {\n onIncrement?.();\n setIsIncrementPressed('mouse');\n } else {\n addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});\n isUp.current = false;\n // For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if\n // the control isn't spinning.\n setIsIncrementPressed('touch');\n }\n addGlobalListener(window, 'contextmenu', cancelContextMenu);\n },\n onPressUp: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n isUp.current = true;\n }\n removeAllGlobalListeners();\n setIsIncrementPressed(null);\n },\n onPressEnd: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n if (!isSpinning.current && isUp.current) {\n onIncrement?.();\n }\n }\n isUp.current = false;\n setIsIncrementPressed(null);\n },\n onFocus,\n onBlur\n },\n decrementButtonProps: {\n onPressStart: (e) => {\n clearAsync();\n if (e.pointerType !== 'touch') {\n onDecrement?.();\n setIsDecrementPressed('mouse');\n } else {\n addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});\n isUp.current = false;\n // For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if\n // the control isn't spinning.\n setIsDecrementPressed('touch');\n }\n },\n onPressUp: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n isUp.current = true;\n }\n removeAllGlobalListeners();\n setIsDecrementPressed(null);\n },\n onPressEnd: (e) => {\n clearAsync();\n if (e.pointerType === 'touch') {\n if (!isSpinning.current && isUp.current) {\n onDecrement?.();\n }\n }\n isUp.current = false;\n setIsDecrementPressed(null);\n },\n onFocus,\n onBlur\n }\n };\n}\n"],"names":[],"version":3,"file":"useSpinButton.module.js.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/spinbutton",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"url": "https://github.com/adobe/react-spectrum"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@react-aria/i18n": "^3.12.
|
|
29
|
+
"@react-aria/i18n": "^3.12.15",
|
|
30
30
|
"@react-aria/live-announcer": "^3.4.4",
|
|
31
|
-
"@react-aria/utils": "^3.
|
|
32
|
-
"@react-types/button": "^3.
|
|
33
|
-
"@react-types/shared": "^3.
|
|
31
|
+
"@react-aria/utils": "^3.33.0",
|
|
32
|
+
"@react-types/button": "^3.15.0",
|
|
33
|
+
"@react-types/shared": "^3.33.0",
|
|
34
34
|
"@swc/helpers": "^0.5.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "66e51757606b43a89ed02c574ca24517323a2ab9"
|
|
44
44
|
}
|
package/src/useSpinButton.ts
CHANGED
|
@@ -15,11 +15,13 @@ import {AriaButtonProps} from '@react-types/button';
|
|
|
15
15
|
import {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared';
|
|
16
16
|
// @ts-ignore
|
|
17
17
|
import intlMessages from '../intl/*.json';
|
|
18
|
-
import {useEffect, useRef} from 'react';
|
|
18
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
19
19
|
import {useEffectEvent, useGlobalListeners} from '@react-aria/utils';
|
|
20
20
|
import {useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
const noop = () => {};
|
|
24
|
+
|
|
23
25
|
export interface SpinButtonProps extends InputBase, Validation<number>, ValueBase<number>, RangeInputBase<number> {
|
|
24
26
|
textValue?: string,
|
|
25
27
|
onIncrement?: () => void,
|
|
@@ -57,11 +59,17 @@ export function useSpinButton(
|
|
|
57
59
|
} = props;
|
|
58
60
|
const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/spinbutton');
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
let isSpinning = useRef(false);
|
|
63
|
+
const clearAsync = useCallback(() => {
|
|
64
|
+
clearTimeout(_async.current);
|
|
65
|
+
isSpinning.current = false;
|
|
66
|
+
}, []);
|
|
67
|
+
const clearAsyncEvent = useEffectEvent(() => {
|
|
68
|
+
clearAsync();
|
|
69
|
+
});
|
|
62
70
|
|
|
63
71
|
useEffect(() => {
|
|
64
|
-
return () =>
|
|
72
|
+
return () => clearAsyncEvent();
|
|
65
73
|
}, []);
|
|
66
74
|
|
|
67
75
|
let onKeyDown = (e) => {
|
|
@@ -135,37 +143,41 @@ export function useSpinButton(
|
|
|
135
143
|
}
|
|
136
144
|
}, [ariaTextValue]);
|
|
137
145
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
);
|
|
146
|
+
// For touch users, if they move their finger like they're scrolling, we don't want to trigger a spin.
|
|
147
|
+
let onPointerCancel = useCallback(() => {
|
|
148
|
+
clearAsync();
|
|
149
|
+
}, [clearAsync]);
|
|
150
|
+
|
|
151
|
+
const onIncrementEvent = useEffectEvent(onIncrement ?? noop);
|
|
152
|
+
const onDecrementEvent = useEffectEvent(onDecrement ?? noop);
|
|
153
|
+
|
|
154
|
+
const stepUpEvent = useEffectEvent(() => {
|
|
155
|
+
if (maxValue === undefined || isNaN(maxValue) || value === undefined || isNaN(value) || value < maxValue) {
|
|
156
|
+
onIncrementEvent();
|
|
157
|
+
onIncrementPressStartEvent(60);
|
|
151
158
|
}
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
initialStepDelay
|
|
166
|
-
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const onIncrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {
|
|
162
|
+
clearAsyncEvent();
|
|
163
|
+
isSpinning.current = true;
|
|
164
|
+
// Start spinning after initial delay
|
|
165
|
+
_async.current = window.setTimeout(stepUpEvent, initialStepDelay);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const stepDownEvent = useEffectEvent(() => {
|
|
169
|
+
if (minValue === undefined || isNaN(minValue) || value === undefined || isNaN(value) || value > minValue) {
|
|
170
|
+
onDecrementEvent();
|
|
171
|
+
onDecrementPressStartEvent(60);
|
|
167
172
|
}
|
|
168
|
-
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const onDecrementPressStartEvent = useEffectEvent((initialStepDelay: number) => {
|
|
176
|
+
clearAsyncEvent();
|
|
177
|
+
isSpinning.current = true;
|
|
178
|
+
// Start spinning after initial delay
|
|
179
|
+
_async.current = window.setTimeout(stepDownEvent, initialStepDelay);
|
|
180
|
+
});
|
|
169
181
|
|
|
170
182
|
let cancelContextMenu = (e) => {
|
|
171
183
|
e.preventDefault();
|
|
@@ -173,6 +185,30 @@ export function useSpinButton(
|
|
|
173
185
|
|
|
174
186
|
let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();
|
|
175
187
|
|
|
188
|
+
// Tracks in touch if the press end event was preceded by a press up.
|
|
189
|
+
// If it wasn't, then we know the finger left the button while still in contact with the screen.
|
|
190
|
+
// This means that the user is trying to scroll or interact in some way that shouldn't trigger
|
|
191
|
+
// an increment or decrement.
|
|
192
|
+
let isUp = useRef(false);
|
|
193
|
+
|
|
194
|
+
let [isIncrementPressed, setIsIncrementPressed] = useState<'touch' | 'mouse' | null>(null);
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (isIncrementPressed === 'touch') {
|
|
197
|
+
onIncrementPressStartEvent(600);
|
|
198
|
+
} else if (isIncrementPressed) {
|
|
199
|
+
onIncrementPressStartEvent(400);
|
|
200
|
+
}
|
|
201
|
+
}, [isIncrementPressed]);
|
|
202
|
+
|
|
203
|
+
let [isDecrementPressed, setIsDecrementPressed] = useState<'touch' | 'mouse' | null>(null);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (isDecrementPressed === 'touch') {
|
|
206
|
+
onDecrementPressStartEvent(600);
|
|
207
|
+
} else if (isDecrementPressed) {
|
|
208
|
+
onDecrementPressStartEvent(400);
|
|
209
|
+
}
|
|
210
|
+
}, [isDecrementPressed]);
|
|
211
|
+
|
|
176
212
|
return {
|
|
177
213
|
spinButtonProps: {
|
|
178
214
|
role: 'spinbutton',
|
|
@@ -188,25 +224,72 @@ export function useSpinButton(
|
|
|
188
224
|
onBlur
|
|
189
225
|
},
|
|
190
226
|
incrementButtonProps: {
|
|
191
|
-
onPressStart: () => {
|
|
192
|
-
|
|
227
|
+
onPressStart: (e) => {
|
|
228
|
+
clearAsync();
|
|
229
|
+
if (e.pointerType !== 'touch') {
|
|
230
|
+
onIncrement?.();
|
|
231
|
+
setIsIncrementPressed('mouse');
|
|
232
|
+
} else {
|
|
233
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});
|
|
234
|
+
isUp.current = false;
|
|
235
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
236
|
+
// the control isn't spinning.
|
|
237
|
+
setIsIncrementPressed('touch');
|
|
238
|
+
}
|
|
193
239
|
addGlobalListener(window, 'contextmenu', cancelContextMenu);
|
|
194
240
|
},
|
|
195
|
-
|
|
241
|
+
onPressUp: (e) => {
|
|
196
242
|
clearAsync();
|
|
243
|
+
if (e.pointerType === 'touch') {
|
|
244
|
+
isUp.current = true;
|
|
245
|
+
}
|
|
197
246
|
removeAllGlobalListeners();
|
|
247
|
+
setIsIncrementPressed(null);
|
|
248
|
+
},
|
|
249
|
+
onPressEnd: (e) => {
|
|
250
|
+
clearAsync();
|
|
251
|
+
if (e.pointerType === 'touch') {
|
|
252
|
+
if (!isSpinning.current && isUp.current) {
|
|
253
|
+
onIncrement?.();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
isUp.current = false;
|
|
257
|
+
setIsIncrementPressed(null);
|
|
198
258
|
},
|
|
199
259
|
onFocus,
|
|
200
260
|
onBlur
|
|
201
261
|
},
|
|
202
262
|
decrementButtonProps: {
|
|
203
|
-
onPressStart: () => {
|
|
204
|
-
|
|
205
|
-
|
|
263
|
+
onPressStart: (e) => {
|
|
264
|
+
clearAsync();
|
|
265
|
+
if (e.pointerType !== 'touch') {
|
|
266
|
+
onDecrement?.();
|
|
267
|
+
setIsDecrementPressed('mouse');
|
|
268
|
+
} else {
|
|
269
|
+
addGlobalListener(window, 'pointercancel', onPointerCancel, {capture: true});
|
|
270
|
+
isUp.current = false;
|
|
271
|
+
// For touch users, don't trigger a decrement on press start, we'll wait for the press end to trigger it if
|
|
272
|
+
// the control isn't spinning.
|
|
273
|
+
setIsDecrementPressed('touch');
|
|
274
|
+
}
|
|
206
275
|
},
|
|
207
|
-
|
|
276
|
+
onPressUp: (e) => {
|
|
208
277
|
clearAsync();
|
|
278
|
+
if (e.pointerType === 'touch') {
|
|
279
|
+
isUp.current = true;
|
|
280
|
+
}
|
|
209
281
|
removeAllGlobalListeners();
|
|
282
|
+
setIsDecrementPressed(null);
|
|
283
|
+
},
|
|
284
|
+
onPressEnd: (e) => {
|
|
285
|
+
clearAsync();
|
|
286
|
+
if (e.pointerType === 'touch') {
|
|
287
|
+
if (!isSpinning.current && isUp.current) {
|
|
288
|
+
onDecrement?.();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
isUp.current = false;
|
|
292
|
+
setIsDecrementPressed(null);
|
|
210
293
|
},
|
|
211
294
|
onFocus,
|
|
212
295
|
onBlur
|