@amaui/ui-react
Version:
UI for React
350 lines (349 loc) • 17.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importDefault(require("react"));
const utils_1 = require("@amaui/utils");
const style_react_1 = require("@amaui/style-react");
const __1 = require("..");
const other = {
pointerEvents: 'none',
borderRadius: 'inherit',
position: 'absolute',
inset: '0',
width: '100%',
height: '100%'
};
const useStyle = (0, style_react_1.style)(theme => ({
'@keyframes pulse': {
'0%': {
transform: 'scale(0.77)'
},
'50%': {
transform: 'scale(0.7)'
},
'100%': {
transform: 'scale(0.77)'
}
},
root: Object.assign(Object.assign({}, other), { overflow: 'hidden',
// Bug fix for overflow in mobile Safari
zIndex: '0' }),
background: Object.assign(Object.assign({}, other), { opacity: theme.palette.visual_contrast.default.opacity.hover, transition: theme.methods.transitions.make(['opacity', 'background'], { duration: 'rg', timing_function: 'standard' }) }),
hovered: {
background: 'currentColor'
},
selected: {
opacity: theme.palette.visual_contrast.default.opacity.selected,
background: 'currentColor'
},
dragged: {
opacity: theme.palette.visual_contrast.default.opacity.drag,
background: 'currentColor'
},
border: Object.assign(Object.assign({}, other), { opacity: '0', boxShadow: 'inset 0 0 0 2px currentColor' }),
wave: Object.assign(Object.assign({}, other), { inset: 'unset', opacity: '0.1', transform: 'scale(0)', backgroundColor: 'currentColor', transition: theme.methods.transitions.make(['opacity', 'transform'], { duration: 'complex', timing_function: 'standard' }), borderRadius: theme.methods.shape.radius.value(4e4, 'px'), '&.entering': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(1)',
}, '&.entered': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(1)',
}, '&.exit': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(1)',
}, '&.exiting': {
opacity: '0',
transform: 'scale(1)',
}, '&.exited': {
opacity: '0',
transform: 'scale(1)',
} }),
pulse: {
'&.entering': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(0.77)',
},
'&.entered': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(0.77)',
animation: `$pulse 2400ms ${theme.transitions.timing_function.emphasized} 240ms infinite`,
},
'&.exit': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
transform: 'scale(0.7)'
},
'&.exiting': {
opacity: '0',
transform: 'scale(0.7)'
},
'&.exited': {
opacity: '0',
transform: 'scale(0.7)'
}
},
waveSimple: Object.assign(Object.assign({}, other), { opacity: '0.1', backgroundColor: 'currentcolor', transition: theme.methods.transitions.make(['opacity'], { duration: 'complex', timing_function: 'standard' }), '&.entering': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
}, '&.entered': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary,
}, '&.exit': {
opacity: theme.palette.visual_contrast.default.opacity.quaternary
}, '&.exiting': {
opacity: '0',
}, '&.exited': {
opacity: '0',
} })
}), { name: 'amaui-Interaction' });
const Interaction = react_1.default.forwardRef((props_, ref) => {
const theme = (0, style_react_1.useAmauiTheme)();
const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.amauiInteraction) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]);
const { wave = true, background = true, border: border_, pulse, origin: origin_, preselected, selected, dragged, wave_version, subscription, clear, disabled, className } = props;
const { classes } = useStyle();
const [init, setInit] = react_1.default.useState(false);
const [interactions, setInteractions] = react_1.default.useState([]);
const [border, setBorder] = react_1.default.useState(false);
const [waves, setWaves] = react_1.default.useState([]);
const refs = {
root: react_1.default.useRef(undefined),
mouse: react_1.default.useRef({ down: 0, up: 0, press: 0 }),
wave: react_1.default.useRef(undefined),
pulse: react_1.default.useRef(undefined),
touch: react_1.default.useRef(undefined),
interactions: react_1.default.useRef(undefined),
props: react_1.default.useRef(undefined)
};
const touch = (0, __1.useMediaQuery)('(pointer: coarse)', { element: refs.root.current });
refs.props.current = props;
refs.wave.current = wave;
refs.pulse.current = pulse;
refs.touch.current = touch;
refs.interactions.current = interactions;
react_1.default.useEffect(() => {
var _a;
const parent = refs.root.current.parentNode;
const onMouseIn = (event) => {
if (refs.touch.current)
return;
add('mouse-in');
removeWaves();
};
const onMouseOut = (event) => {
if (refs.touch.current)
return;
refs.mouse.current.up = new Date().getTime();
refs.mouse.current.press = refs.mouse.current.down ? Math.round(refs.mouse.current.up - refs.mouse.current.down) : 0;
remove('mouse-in');
removeWaves();
};
const onMouseDown = (event) => {
refs.mouse.current.down = new Date().getTime();
refs.mouse.current.up = 0;
refs.mouse.current.press = 0;
add('mouse-down');
addWave(event);
};
const updateBorder = (0, utils_1.debounce)(() => setBorder(false), theme.transitions.duration.sm);
const onMouseUp = () => {
refs.mouse.current.up = new Date().getTime();
refs.mouse.current.press = refs.mouse.current.down ? Math.round(refs.mouse.current.up - refs.mouse.current.down) : 0;
setInteractions(items => {
if (items.indexOf('mouse-down') > -1) {
// Border
setBorder(true);
updateBorder();
}
return items;
});
remove('mouse-down');
removeWaves();
};
const rootDocument = (0, utils_1.isEnvironment)('browser') ? (((_a = refs.root.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || window.document) : undefined;
if (parent) {
parent.addEventListener('mousedown', onMouseDown);
parent.addEventListener('touchstart', onMouseDown, { passive: true });
parent.addEventListener('mouseup', onMouseUp);
rootDocument.addEventListener('mouseup', onMouseUp);
parent.addEventListener('touchend', onMouseUp, { passive: true });
rootDocument.addEventListener('touchend', onMouseUp, { passive: true });
parent.addEventListener('mouseenter', onMouseIn);
// parent.addEventListener('touchstart', onMouseIn, { passive: true });
parent.addEventListener('mouseleave', onMouseOut);
// parent.addEventListener('touchend', onMouseOut, { passive: true });
}
setInit(true);
return () => {
if (parent) {
parent.removeEventListener('mousedown', onMouseDown);
parent.removeEventListener('touchstart', onMouseDown);
parent.removeEventListener('mouseup', onMouseUp);
rootDocument.removeEventListener('mouseup', onMouseUp);
parent.removeEventListener('touchend', onMouseUp);
rootDocument.removeEventListener('touchend', onMouseUp);
parent.removeEventListener('mouseenter', onMouseIn);
parent.removeEventListener('touchstart', onMouseIn);
parent.removeEventListener('mouseleave', onMouseOut);
parent.removeEventListener('touchend', onMouseOut);
}
};
}, []);
react_1.default.useEffect(() => {
if (init) {
setInteractions([]);
removeWaves();
}
}, [clear]);
react_1.default.useEffect(() => {
if (pulse) {
if (!refs.props.current.pulseOnMouseDown && has('mouse-down'))
return;
addWavePulse();
}
else
removeWaves();
}, [pulse]);
const methods = react_1.default.useCallback((version) => {
if (version === 'add')
addWave(undefined, { origin: 'center' });
else if (version === 'pulse')
addWavePulse();
else if (version === 'remove')
removeWaves();
}, []);
react_1.default.useEffect(() => {
let subscribed;
if (subscription === null || subscription === void 0 ? void 0 : subscription.subscribe)
subscribed = subscription.subscribe(methods);
// Clean up
return () => {
if (subscribed === null || subscribed === void 0 ? void 0 : subscribed.unsubscribe)
subscribed.unsubscribe();
};
}, [subscription]);
react_1.default.useEffect(() => {
if (disabled)
setInteractions([]);
}, [disabled]);
const addWave = react_1.default.useCallback((event, overrides = {}) => {
var _a, _b;
const origin = overrides.origin !== undefined ? overrides.origin : origin_;
if (refs.wave.current && !refs.props.current.disabled) {
let top = 0;
let left = 0;
let width = '100%';
if (wave_version !== 'simple') {
const rect = (_a = event === null || event === void 0 ? void 0 : event.currentTarget) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
const root = (_b = (((event === null || event === void 0 ? void 0 : event.currentTarget) || refs.root.current.parentNode))) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
// Mouse or touch event
const valuesEvent = (!(event === null || event === void 0 ? void 0 : event.touches) ? event : event.touches[0]);
const values = {
x: valuesEvent.x !== undefined ? valuesEvent.x : valuesEvent.clientX,
y: valuesEvent.y !== undefined ? valuesEvent.y : valuesEvent.clientY
};
values.offsetX = valuesEvent.offsetX !== undefined ? valuesEvent.offsetX : values.x - rect.x;
values.offsetY = valuesEvent.offsetY !== undefined ? valuesEvent.offsetY : values.y - rect.y;
const w = root.width;
const h = root.height;
const x = (origin === 'center' || !event) ? w / 2 : rect ? values.x - rect.left : values.offsetX;
const y = (origin === 'center' || !event) ? h / 2 : rect ? values.y - rect.top : values.offsetY;
width = Math.round(Math.sqrt(
// Largest sub rectangle in value
Math.max(x ** 2 + y ** 2, Math.abs(w - x) ** 2 + y ** 2, x ** 2 + Math.abs(h - y) ** 2, Math.abs(w - x) ** 2 + Math.abs(h - y) ** 2))
* 2);
top = y - width / 2;
left = x - width / 2;
}
setWaves(items => [
...items,
((0, jsx_runtime_1.jsx)(__1.Transition, Object.assign({ duration: 'complex', enterOnAdd: true, className: true }, { children: (0, jsx_runtime_1.jsx)("span", { className: wave_version === 'simple' ? classes.waveSimple : classes.wave, style: {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${width}px`
} }) }), (0, utils_1.getID)()))
]);
}
}, []);
const addWavePulse = react_1.default.useCallback(() => {
if (refs.pulse.current && !refs.props.current.disabled) {
// Remove previous wave
// if there is one
if (waves.length)
removeWaves();
const root = refs.root.current.parentNode.getBoundingClientRect();
const w = root.width;
const h = root.height;
const x = w / 2;
const y = h / 2;
const width = Math.round(Math.sqrt(x ** 2 + y ** 2)
* 2);
const top = y - width / 2;
const left = x - width / 2;
setWaves(items => [
...items,
((0, jsx_runtime_1.jsx)(__1.Transition, Object.assign({ duration: 'complex', enterOnAdd: true, className: true }, { children: (0, jsx_runtime_1.jsx)("span", { className: (0, style_react_1.classNames)([classes.wave, classes.pulse]), style: {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${width}px`
} }) }), (0, utils_1.getID)()))
]);
}
}, []);
const removeWaves = react_1.default.useCallback(() => setWaves([]), []);
const add = react_1.default.useCallback((value) => {
if (!refs.props.current.disabled) {
setInteractions(items => {
const newItems = [...items];
if (newItems.indexOf(value) === -1)
newItems.push(value);
return newItems;
});
}
}, []);
const has = react_1.default.useCallback((value) => refs.interactions.current.includes(value), []);
const remove = react_1.default.useCallback((value) => setInteractions(items => [...items].filter(item => item !== value)), []);
return ((0, jsx_runtime_1.jsxs)("span", Object.assign({ ref: item => {
if (ref) {
if ((0, utils_1.is)('function', ref))
ref(item);
else
ref.current = item;
}
refs.root.current = item;
}, className: (0, style_react_1.classNames)([
(0, __1.staticClassName)('Interaction', theme) && [
'amaui-Interaction-root',
disabled && `amaui-Interaction-disabled`
],
className,
classes.root
]) }, { children: [background && ((0, jsx_runtime_1.jsx)("span", { className: (0, style_react_1.classNames)([
(0, __1.staticClassName)('Interaction', theme) && [
'amaui-Interaction-background'
],
classes.background,
(preselected || has('mouse-in')) && classes.hovered,
selected && classes.selected,
dragged && classes.dragged
]) })), (0, jsx_runtime_1.jsx)(__1.Transitions, Object.assign({ TransitionProps: {
noAbruption: true,
duration: {
enter: 'complex',
exit: refs.mouse.current.press < 500 ? 350 : 500
},
style: {
transitionDuration: refs.mouse.current.press < 500 ? 340 : 500
}
} }, { children: waves })), border_ && ((0, jsx_runtime_1.jsx)(__1.Transition, Object.assign({ in: border }, { children: (status) => ((0, jsx_runtime_1.jsx)("span", { className: (0, style_react_1.classNames)([
(0, __1.staticClassName)('Interaction', theme) && [
'amaui-Interaction-border'
],
classes.border
]), style: {
opacity: (status === null || status === void 0 ? void 0 : status.indexOf('enter')) > -1 ? theme.palette.visual_contrast.default.opacity.quaternary : 0,
transition: (status === null || status === void 0 ? void 0 : status.indexOf('exit')) > -1 ? theme.methods.transitions.make('opacity', { duration: 'complex', timing_function: 'standard' }) : undefined
} })) })))] })));
});
Interaction.displayName = 'amaui-Interaction';
exports.default = Interaction;