react-use#useEvent TypeScript Examples
The following examples show how to use
react-use#useEvent.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Slider.tsx From UUI with MIT License | 4 votes |
Slider = UUIFunctionComponent({
name: 'Slider',
nodes: {
Root: 'div',
Container: 'div',
ActiveLine: 'div',
InactiveLine: 'div',
Thumb: 'div',
Remark: 'div',
RemarkLabel: 'div',
},
propTypes: SliderPropTypes,
}, (props: SliderFeatureProps, { nodes, NodeDataProps }) => {
const { Root, Container, ActiveLine, InactiveLine, Thumb, Remark, RemarkLabel } = nodes
const finalProps = {
remarks: props.remarks || [],
}
/**
* Due to Slider supports the selection of a value or a range of values,
* a unified interface is needed to handle what type of data the component should return.
*/
const finalValue = useMemo(() => {
return [
typeof props.value === 'number' ? props.min : props.value[0],
typeof props.value === 'number' ? props.value : props.value[1],
] as const
}, [props.min, props.value])
const onFinalChange = useCallback((value: [number, number]) => {
const newValue: [number, number] = [Number(value[0].toFixed(8)), Number(value[1].toFixed(8))]
if (typeof props.value === 'number') {
props.onChange.call(undefined, newValue[1])
} else {
props.onChange.call(undefined, newValue)
}
}, [props.value, props.onChange])
/**
* Handle thumbs position
*/
const [thumbDragging, setThumbDragging] = useState<0 | 1 | null>(null)
const [finalPosition, setFinalPosition] = useState<[number, number]>([
(finalValue[0]-props.min) / (props.max-props.min),
(finalValue[1]-props.min) / (props.max-props.min),
])
useEffect(() => {
setFinalPosition([
(finalValue[0]-props.min) / (props.max-props.min),
(finalValue[1]-props.min) / (props.max-props.min),
])
}, [finalValue, props.min, props.max])
const containerRef = useRef<any>()
const getPositionFromEvent = (event: MouseEvent | TouchEvent) => {
if (!containerRef.current) return null
const containerRect = containerRef.current.getBoundingClientRect()
const leadingPosition = props.vertical ? containerRect.bottom : containerRect.left
const trailingPosition = props.vertical ? containerRect.top : containerRect.right
const currentPosition = (() => {
switch (event.type) {
case 'mousedown':
case 'mousemove':
case 'click':
return props.vertical ? (event as MouseEvent).clientY : (event as MouseEvent).clientX
case 'touchstart':
case 'touchmove':
return props.vertical ? (event as TouchEvent).touches[0].clientY :(event as TouchEvent).touches[0].clientX
default:
return null
}
})();
if (!currentPosition) return null
let newPosition = (currentPosition - leadingPosition) / (trailingPosition - leadingPosition)
newPosition = clamp(newPosition, 0.00, 1.00)
return newPosition
}
const onEventPositionChange = (position: number, thumbIndex: 0 | 1) => {
const newValue = Math.round((props.max-props.min) / props.step * position) * props.step + props.min
setFinalPosition((value) => {
value[thumbIndex] = position
return value
})
if (newValue !== props.value) {
const newFinalValue: [number, number] = [finalValue[0], finalValue[1]]
newFinalValue[thumbIndex] = newValue
onFinalChange(newFinalValue)
}
}
const onMouseDownOrTouchStart = (event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.TouchEvent<HTMLDivElement>) => {
if (props.disabled) return;
const newPosition = getPositionFromEvent(event as any)
if (!newPosition) return;
const targetIndex = isArray(props.value)
? (Math.abs(finalPosition[0] - newPosition) < Math.abs(finalPosition[1] - newPosition) ? 0 : 1)
: 1;
!props.disabled && setThumbDragging(targetIndex)
onEventPositionChange(newPosition, targetIndex)
}
const onMouseUpOrTouchEnd = () => { !props.disabled && setThumbDragging(null) }
const onMouseOrTouchMove = (event: MouseEvent | TouchEvent) => {
if (props.disabled) return;
if (thumbDragging === null) return;
const newPosition = getPositionFromEvent(event)
if (newPosition === null) return;
onEventPositionChange(newPosition, thumbDragging)
}
useEvent('mousemove', onMouseOrTouchMove as any, window, { capture: !props.disabled && !!thumbDragging })
useEvent('touchmove', onMouseOrTouchMove as any, window, { capture: !props.disabled && !!thumbDragging })
useEvent('mouseup', onMouseUpOrTouchEnd as any, window, { capture: !props.disabled && !!thumbDragging })
useEvent('touchend', onMouseUpOrTouchEnd as any, window, { capture: !props.disabled && !!thumbDragging })
/**
* Calculate the position and size of thumbs, remarks and lines.
*/
const styles = useMemo(() => {
const sortPosition = clone(finalPosition).sort()
switch (props.vertical) {
case false:
case undefined:
return {
LeadingInactiveLine: {
width: toPercentage(sortPosition[0]),
display: typeof props.value === 'number' ? 'none' : undefined,
},
ActiveLine: {
width: toPercentage(sortPosition[1] - sortPosition[0]),
},
TrailingInactiveLine: {
width: toPercentage(1 - sortPosition[1]),
},
LeadingThumb: {
left: toPercentage(finalPosition[0]),
display: typeof props.value === 'number' ? 'none' : undefined,
transform: 'translateX(-50%)',
},
TrailingThumb: {
left: toPercentage(finalPosition[1]),
transform: 'translateX(-50%)',
},
Remark: finalProps.remarks.map((remark) => {
const position = (remark.value - props.min) / (props.max - props.min)
return {
left: toPercentage(position),
transform: 'translateX(-50%)',
}
})
}
case true:
return {
TrailingInactiveLine: {
height: toPercentage(sortPosition[0]),
display: typeof props.value === 'number' ? 'none' : undefined,
},
ActiveLine: {
height: toPercentage(sortPosition[1] - sortPosition[0]),
},
LeadingInactiveLine: {
height: toPercentage(1 - sortPosition[1]),
},
LeadingThumb: {
bottom: toPercentage(finalPosition[0]),
display: typeof props.value === 'number' ? 'none' : undefined,
transform: 'translateY(50%)',
},
TrailingThumb: {
bottom: toPercentage(finalPosition[1]),
transform: 'translateY(50%)',
},
Remark: finalProps.remarks.map((remark) => {
const position = (remark.value - props.min) / (props.max - props.min)
return {
bottom: toPercentage(position),
transform: 'translateY(50%)',
}
})
}
}
}, [finalPosition, props.value, props.max, props.min, props.vertical, finalProps.remarks])
const [focusThumbIndex, setFocusThumbIndex] = useState<number | null>(null)
return (
<Root
{...NodeDataProps({
'disabled': !!props.disabled,
'vertical': !!props.vertical,
})}
onMouseDown={onMouseDownOrTouchStart}
onTouchStart={onMouseDownOrTouchStart}
onKeyDown={(event) => {
switch (event.keyCode) {
case KeyCode.ArrowLeft:
case KeyCode.ArrowDown: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = clamp(newValue[focusThumbIndex] - props.step, props.min, props.max);
onFinalChange(newValue)
}
break
}
case KeyCode.ArrowRight:
case KeyCode.ArrowUp: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = clamp(newValue[focusThumbIndex] + props.step, props.min, props.max);
onFinalChange(newValue)
}
break
}
case KeyCode.PageDown: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = clamp(newValue[focusThumbIndex] - props.step * 10, props.min, props.max);
onFinalChange(newValue)
}
break
}
case KeyCode.PageUp: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = clamp(newValue[focusThumbIndex] + props.step * 10, props.min, props.max);
onFinalChange(newValue)
}
break
}
case KeyCode.Home: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = props.max;
onFinalChange(newValue)
}
break
}
case KeyCode.End: {
if (focusThumbIndex !== null) {
const newValue = Array.from(finalValue) as [number, number];
newValue[focusThumbIndex] = props.min;
onFinalChange(newValue)
}
break
}
default:
// do nothing
}
}}
onFocus={props.onFocus}
onBlur={props.onBlur}
>
<Container ref={containerRef}>
<InactiveLine style={{ ...styles.LeadingInactiveLine }} />
<ActiveLine style={{ ...styles.ActiveLine }} />
<InactiveLine style={{ ...styles.TrailingInactiveLine }} />
{finalProps.remarks.map((remark, index) => {
const isActive = inRange(remark.value, finalValue[0], finalValue[1])
return (
<Remark
key={index}
{...NodeDataProps({
'active': !!isActive,
})}
style={{ ...styles.Remark[index] }}
>
<RemarkLabel>{remark.label}</RemarkLabel>
</Remark>
)
})}
<Thumb
role="slider"
aria-orientation={props.vertical ? "vertical" : "horizontal"}
aria-valuemin={props.min}
aria-valuemax={props.max}
aria-valuenow={finalValue[0]}
aria-valuetext={`${finalValue[0]}`}
tabIndex={props.disabled ? -1 : 0}
style={{ ...styles.LeadingThumb }}
onFocus={() => { setFocusThumbIndex(0) }}
onBlur={() => { setFocusThumbIndex(null)}}
/>
<Thumb
role="slider"
aria-orientation={props.vertical ? "vertical" : "horizontal"}
aria-valuemin={props.min}
aria-valuemax={props.max}
aria-valuenow={finalValue[1]}
aria-valuetext={`${finalValue[1]}`}
tabIndex={props.disabled ? -1 : 0}
style={{ ...styles.TrailingThumb }}
onFocus={() => { setFocusThumbIndex(1) }}
onBlur={() => { setFocusThumbIndex(null)}}
/>
</Container>
</Root>
)
})
Example #2
Source File: TorrentList.tsx From flood with GNU General Public License v3.0 | 4 votes |
TorrentList: FC = observer(() => {
const listHeaderRef = useRef<HTMLDivElement>(null);
const listViewportRef = useRef<FixedSizeList>(null);
const listViewportOuterRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const dispose = reaction(
() => TorrentFilterStore.filterTrigger,
() => {
if (listViewportRef.current != null) {
listViewportRef.current.scrollTo(0);
}
},
);
return dispose;
}, []);
useEvent('keydown', (e: KeyboardEvent) => {
const {ctrlKey, key, metaKey, repeat, target} = e;
const tagName = (target as HTMLElement)?.tagName.toUpperCase();
if (tagName === 'INPUT' || tagName === 'TEXTAREA') {
return;
}
if (repeat) {
return;
}
if ((metaKey || ctrlKey) && key === 'a') {
e.preventDefault();
TorrentStore.selectAllTorrents();
}
});
const torrents = TorrentStore.filteredTorrents;
const {torrentListViewSize = 'condensed'} = SettingStore.floodSettings;
const isCondensed = torrentListViewSize === 'condensed';
const isListEmpty = torrents == null || torrents.length === 0;
let content: ReactNode = null;
let torrentListHeading: ReactNode = null;
if (!ClientStatusStore.isConnected) {
content = (
<div className="torrents__alert__wrapper">
<div className="torrents__alert">
<Trans id="torrents.list.cannot.connect" />
</div>
</div>
);
} else if (isListEmpty || torrents == null) {
content = (
<div className="torrents__alert__wrapper">
<div className="torrents__alert">
<Trans id="torrents.list.no.torrents" />
</div>
{TorrentFilterStore.isFilterActive ? (
<div className="torrents__alert__action">
<Button
onClick={() => {
TorrentFilterStore.clearAllFilters();
}}
priority="tertiary"
>
<Trans id="torrents.list.clear.filters" />
</Button>
</div>
) : null}
</div>
);
} else {
if (isCondensed) {
torrentListHeading = (
<TableHeading
onCellFocus={() => {
if (listViewportOuterRef.current != null && listHeaderRef.current != null) {
listViewportOuterRef.current.scrollLeft = listHeaderRef.current.scrollLeft;
}
}}
onCellClick={(property: TorrentListColumn) => {
const currentSort = SettingStore.floodSettings.sortTorrents;
let nextDirection = SortDirections[property] ?? 'asc';
if (currentSort.property === property) {
nextDirection = currentSort.direction === 'asc' ? 'desc' : 'asc';
}
const sortBy = {
property,
direction: nextDirection,
};
SettingActions.saveSetting('sortTorrents', sortBy);
}}
onWidthsChange={(column: TorrentListColumn, width: number) => {
const {torrentListColumnWidths = defaultFloodSettings.torrentListColumnWidths} = SettingStore.floodSettings;
SettingActions.saveSetting('torrentListColumnWidths', {
...torrentListColumnWidths,
[column]: width,
});
}}
ref={listHeaderRef}
/>
);
}
// itemSize must sync with styles &--is-condensed and &--is-expanded
content = (
<ListViewport
className="torrent__list__viewport"
itemCount={torrents.length}
itemKey={(index) => TorrentStore.filteredTorrents[index].hash}
itemRenderer={TorrentListRowRenderer}
itemSize={isCondensed ? 30 : 70}
ref={listViewportRef}
outerRef={(ref) => {
const viewportDiv = ref;
if (viewportDiv != null && viewportDiv.onscroll == null) {
viewportDiv.onscroll = () => {
if (listHeaderRef.current != null) {
listHeaderRef.current.scrollLeft = viewportDiv.scrollLeft;
}
};
}
listViewportOuterRef.current = viewportDiv;
}}
/>
);
}
return (
<TorrentListDropzone>
<div className="torrent__list__wrapper" role="table">
<ContextMenuMountPoint id="torrent-list-item" />
{torrentListHeading}
{content}
</div>
<div className="dropzone__overlay">
<div className="dropzone__copy">
<div className="dropzone__icon">
<Files />
</div>
<Trans id="torrents.list.drop" />
</div>
</div>
</TorrentListDropzone>
);
})
Example #3
Source File: Select.tsx From flood with GNU General Public License v3.0 | 4 votes |
Select: FC<SelectProps> = ({
additionalClassNames,
children,
defaultID,
disabled,
label,
labelOffset,
persistentPlaceholder,
priority,
shrink,
grow,
matchTriggerWidth,
width,
id,
menuAlign,
onOpen,
onClose,
onSelect,
}: SelectProps) => {
const menuRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const triggerRef = useRef<HTMLButtonElement>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [selectedID, setSelectedID] = useState<string | number>(
defaultID ??
(
(children as ReactNodeArray)?.find(
(child) => (child as ReactElement<SelectItemProps>)?.props?.id != null,
) as ReactElement<SelectItemProps>
)?.props.id ??
'',
);
const classes = classnames('select form__element', additionalClassNames, {
'form__element--disabled': disabled,
'form__element--label-offset': labelOffset,
'select--is-open': isOpen,
});
const selectedItem = Children.toArray(children).find((child, index) => {
const item = child as ReactElement<SelectItemProps>;
return (
(persistentPlaceholder && item.props.isPlaceholder) ||
(!selectedID && index === 0) ||
item.props.id === selectedID
);
});
useKey('Escape', (event) => {
event.preventDefault();
setIsOpen(false);
});
useEvent(
'scroll',
(event: Event) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
},
window,
{capture: true},
);
useEffect(() => {
if (isOpen) {
onOpen?.();
} else {
onClose?.();
}
}, [isOpen, onClose, onOpen]);
useEffect(() => {
onSelect?.(selectedID);
if (inputRef.current != null) {
dispatchChangeEvent(inputRef.current);
}
}, [onSelect, selectedID]);
return (
<FormRowItem shrink={shrink} grow={grow} width={width}>
{label && (
<label className="form__element__label" htmlFor={`${id}`}>
{label}
</label>
)}
<div className={classes}>
<input
className="input input--hidden"
name={`${id}`}
onChange={() => undefined}
tabIndex={-1}
ref={inputRef}
type="text"
value={selectedID}
/>
<Button
additionalClassNames="select__button"
buttonRef={triggerRef}
addonPlacement="after"
onClick={() => {
if (!disabled) {
setIsOpen(!isOpen);
}
}}
priority={priority}
wrap={false}
>
<FormElementAddon className="select__indicator">
<Chevron />
</FormElementAddon>
{selectedItem && cloneElement(selectedItem as ReactElement, {isTrigger: true})}
</Button>
<Portal>
<ContextMenu
onOverlayClick={() => {
setIsOpen(!isOpen);
}}
isIn={isOpen}
matchTriggerWidth={matchTriggerWidth}
menuAlign={menuAlign}
ref={menuRef}
triggerRef={triggerRef}
>
{Children.toArray(children).reduce((accumulator: Array<ReactElement>, child) => {
const item = child as ReactElement<SelectItemProps>;
if (item.props.isPlaceholder) {
return accumulator;
}
accumulator.push(
cloneElement(child as ReactElement, {
onClick: (selection: string | number) => {
setIsOpen(false);
setSelectedID(selection);
},
isSelected: item.props.id === selectedID,
}),
);
return accumulator;
}, [])}
</ContextMenu>
</Portal>
</div>
</FormRowItem>
);
}