@amaui/ui-react
Version:
UI for React
429 lines (428 loc) • 21.7 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
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 style_react_1 = require("@amaui/style-react");
const utils_1 = require("@amaui/utils");
const date_1 = require("@amaui/date");
const RoundMeter_1 = __importDefault(require("../RoundMeter"));
const Path_1 = __importDefault(require("../Path"));
const utils_2 = require("../utils");
const useStyle = (0, style_react_1.style)(theme => ({
root: {
userSelect: 'none',
touchAction: 'none',
'& .amaui-RoundMeter-children, & .amaui-RoundMeter-labels': {
pointerEvents: 'none'
},
'& svg > *': {
cursor: 'grab'
}
},
mouseDown: {
'& svg > *': {
cursor: 'grabbing'
}
},
}), { name: 'amaui-Clock' });
const Clock = 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.amauiClock) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props__)); }, [props__]);
const RoundMeter = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.RoundMeter) || RoundMeter_1.default; }, [theme]);
const Path = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Path) || Path_1.default; }, [theme]);
const { tonal = true, color = 'primary', size = 'regular', value: value_, valueDefault, onChange, selecting: selecting_, selectingDefault, onChangeSelecting, format = '12', dayTime = 'am', hour = true, minute = true, second = false, autoNext, min, max, validate, readOnly, disabled, valid: valid_, renderValue, onDoneSelecting, onClick: onClick_, className, BackgroundProps } = props, other = __rest(props, ["tonal", "color", "size", "value", "valueDefault", "onChange", "selecting", "selectingDefault", "onChangeSelecting", "format", "dayTime", "hour", "minute", "second", "autoNext", "min", "max", "validate", "readOnly", "disabled", "valid", "renderValue", "onDoneSelecting", "onClick", "className", "BackgroundProps"]);
const { classes } = useStyle();
const [value, setValue] = react_1.default.useState((valueDefault !== undefined ? valueDefault : value_) || new date_1.AmauiDate());
const [selecting, setSelecting] = react_1.default.useState((selectingDefault !== undefined ? selectingDefault : selecting_) || 'hour');
const [mouseDown, setMouseDown] = react_1.default.useState(false);
const refs = {
root: react_1.default.useRef(undefined),
middle: react_1.default.useRef(undefined),
mouseDown: react_1.default.useRef(undefined),
value: react_1.default.useRef(),
selecting: react_1.default.useRef(),
autoNext: react_1.default.useRef(),
hour: react_1.default.useRef(),
minute: react_1.default.useRef(),
second: react_1.default.useRef(),
format: react_1.default.useRef(),
dayTime: react_1.default.useRef(undefined),
previous: react_1.default.useRef(selecting)
};
refs.mouseDown.current = mouseDown;
refs.value.current = value;
refs.hour.current = hour;
refs.minute.current = minute;
refs.second.current = second;
refs.selecting.current = selecting;
refs.autoNext.current = autoNext;
refs.format.current = format;
refs.dayTime.current = dayTime;
const resolve = react_1.default.useCallback((valueNew = refs.value.current, dayTimeValue = refs.dayTime.current) => {
// Resolve the range value
const valueHour = valueNew.hour;
if (format === '12') {
if (dayTimeValue === 'am' && valueHour > 12)
return (0, date_1.set)(valueHour - 12, 'hour', valueNew);
if (dayTimeValue === 'pm' && valueHour < 12)
return (0, date_1.set)(valueHour + 12, 'hour', valueNew);
}
return valueNew;
}, [value, dayTime, format]);
const inputToValue = react_1.default.useCallback((valueNew, unit = refs.selecting.current) => {
let amauiDate = new date_1.AmauiDate(refs.value.current);
let valueTime = valueNew;
if ((0, utils_1.is)('string', valueTime) && valueTime.startsWith('0'))
valueTime = valueTime.slice(1);
valueTime = +valueTime;
if (unit === 'hour')
amauiDate = (0, date_1.set)((format === '12' && dayTime === 'pm') ? valueTime + 12 : valueTime, 'hour', amauiDate);
else
amauiDate = (0, date_1.set)(valueTime, unit, amauiDate);
return resolve(amauiDate);
}, [value, format, dayTime, hour, minute, second]);
const onMove = react_1.default.useCallback((x_, y_) => {
const rectMiddle = refs.middle.current.getBoundingClientRect();
const x = x_ - rectMiddle.x;
const y = y_ - rectMiddle.y;
const radians = Math.atan2(x, y);
const degrees = (radians * 180) / Math.PI;
const angle = 180 - degrees;
// Make array of values
// for hours, minutes and seconds
// with +- 50% around the value
// Find item in that array that this angle fits within
let valuesAll = [];
if (refs.selecting.current === 'hour') {
const part = 360 / 12;
valuesAll = Array.from({ length: 12 }).map((item, index_) => [(part * index_) - (part / 2), (part * index_) + (part / 2)]);
let index = valuesAll.findIndex((item) => angle >= item[0] && angle <= item[1]);
if (index === -1 || index === 0)
index = refs.format.current === '24' ? 0 : 12;
if (refs.format.current === '24') {
let within = false;
const labelElements = refs.root.current.querySelectorAll('.amaui-RoundMeter-labels');
const elements = {
outer: labelElements[0],
inner: labelElements[1]
};
const rects = {
outer: elements.outer.getBoundingClientRect(),
inner: elements.inner.getBoundingClientRect()
};
const part_ = Math.abs(Math.abs(rects.outer.x) - Math.abs(rects.inner.x));
const valueMoved = Math.sqrt(x ** 2 + y ** 2);
const middleInner = Math.abs(Math.abs(rectMiddle.x) - Math.abs(rects.inner.x));
if (valueMoved <= (middleInner + (part_ / 2)))
within = true;
if (within)
index += 12;
index = (0, utils_1.clamp)(index, 0, 23);
}
// Validate
if (!valid(inputToValue(index, 'hour'), 'hour'))
return;
// Update values
onUpdate(inputToValue(index, 'hour'));
}
else if (['minute', 'second'].includes(refs.selecting.current)) {
const part = 360 / 60;
valuesAll = Array.from({ length: 60 }).map((item, index_) => [(part * index_) - (part / 2), (part * index_) + (part / 2)]);
let index = valuesAll.findIndex((item) => angle >= item[0] && angle <= item[1]);
if (index === -1 || index === 0)
index = 0;
// Validate
if (!valid(inputToValue(index), refs.selecting.current))
return;
// Update values
onUpdate(inputToValue(index));
}
}, []);
react_1.default.useEffect(() => {
var _a;
const onMouseUp = () => {
if (refs.mouseDown.current) {
setMouseDown(false);
// Auto next
if (refs.autoNext.current) {
if (['hour', 'minute', 'second'].includes(refs.selecting.current)) {
let valueSelecting;
if (refs.selecting.current === 'second')
valueSelecting = 'hour';
if (refs.selecting.current === 'minute')
valueSelecting = refs.second.current ? 'second' : 'hour';
if (refs.selecting.current === 'hour' && refs.minute.current)
valueSelecting = 'minute';
onUpdateSelecting(valueSelecting);
}
}
if ((0, utils_1.is)('function', onDoneSelecting))
onDoneSelecting(refs.value.current, refs.selecting.current);
}
};
// Mouse move
const onMouseMove = (event) => {
if (refs.mouseDown.current) {
const { clientY, clientX } = event;
onMove(clientX, clientY);
}
};
// Touch move
const onTouchMove = (event) => {
if (refs.mouseDown.current) {
const { clientY, clientX } = event.touches[0];
onMove(clientX, clientY);
}
};
const rootDocument = (0, utils_1.isEnvironment)('browser') ? (((_a = refs.root.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || window.document) : undefined;
rootDocument.addEventListener('mouseup', onMouseUp);
rootDocument.addEventListener('mousemove', onMouseMove);
rootDocument.addEventListener('touchend', onMouseUp);
rootDocument.addEventListener('touchmove', onTouchMove, { passive: true });
return () => {
rootDocument.removeEventListener('mousemove', onMouseMove);
rootDocument.removeEventListener('mouseup', onMouseUp);
rootDocument.removeEventListener('touchmove', onTouchMove);
rootDocument.removeEventListener('touchend', onMouseUp);
};
}, []);
react_1.default.useEffect(() => {
if (value_ !== undefined && value_ !== value)
setValue(value_);
}, [value_]);
const updateTransitions = react_1.default.useCallback(() => {
// Add momentary transition to the AmauiRoundMeter-children > *
// if selecting value updates
if (refs.root.current) {
let elementChildren = refs.root.current.getElementsByClassName('amaui-RoundMeter-children')[0];
let elementLabels = refs.root.current.getElementsByClassName('amaui-RoundMeter-labels')[0];
if (elementChildren && elementLabels) {
elementChildren = Array.from(elementChildren.children);
elementLabels = Array.from(elementLabels.children);
elementChildren.forEach((item) => item.style.transition = 'transform .3s');
elementLabels.forEach((item) => item.style.transition = 'fill .3s');
setTimeout(() => {
[...elementChildren, ...elementLabels].forEach((item) => item.style.removeProperty('transition'));
}, 300);
}
}
}, []);
react_1.default.useEffect(() => {
if (selecting_ !== undefined && selecting_ !== selecting) {
setSelecting(selecting_);
refs.previous.current = selecting_;
updateTransitions();
}
}, [selecting_]);
react_1.default.useEffect(() => {
if (selecting !== refs.previous.current) {
refs.previous.current = selecting;
updateTransitions();
}
}, [selecting]);
const onUpdate = react_1.default.useCallback((valueNew) => {
const newValue = valueNew.milliseconds;
const previousValue = refs.value.current.milliseconds;
if (newValue === previousValue)
return;
if (!(readOnly || disabled)) {
// Inner controlled value
if (!props.hasOwnProperty('value'))
setValue(valueNew);
if ((0, utils_1.is)('function', onChange))
onChange(valueNew);
}
}, [readOnly, disabled]);
const onUpdateSelecting = react_1.default.useCallback((valueNew) => {
if (!(readOnly || disabled)) {
// Inner controlled selecting
if (!props.hasOwnProperty('selecting'))
setSelecting(valueNew);
if ((0, utils_1.is)('function', onChangeSelecting))
onChangeSelecting(valueNew);
}
}, [readOnly, disabled]);
const valid = react_1.default.useCallback((...args) => {
if ((0, utils_1.is)('function', valid_))
return valid_(...args);
const amauiDate = args[0];
if (min || max || validate) {
let response = true;
if ((0, utils_1.is)('function', validate))
response = validate(amauiDate);
if (min !== undefined)
response = response && (0, date_1.is)(amauiDate, 'after or same', min);
if (max !== undefined)
response = response && (0, date_1.is)(amauiDate, 'before or same', max);
return response;
}
return true;
}, [valid_, min, max, validate]);
const onMouseDown = react_1.default.useCallback(() => {
setMouseDown(true);
}, []);
const onClick = react_1.default.useCallback((event) => {
const { clientX: x, clientY: y } = event;
onMove(x, y);
if ((0, utils_1.is)('function', onClick_))
onClick_(event);
}, [onClick_]);
const palette = react_1.default.useMemo(() => {
var _a;
if (['inherit', 'default'].includes(color))
return theme.methods.color(theme.palette.text.default.primary);
if (color === 'themed')
return theme.methods.color(theme.palette.text.default.secondary);
if (color === 'inverted')
return theme.methods.color(theme.palette.background.default.primary);
return theme.methods.color(((_a = theme.palette.color[color]) === null || _a === void 0 ? void 0 : _a.main) || color);
}, [color, theme]);
let valueClock = '';
let valueClock24 = 0;
let valuePosition;
let labels = [];
let lowerPointer = false;
const colors = {
regular: 'currentColor',
inverse: theme.methods.palette.color.value(undefined, 90, true, palette)
};
if (selecting === 'hour') {
// Value
valueClock = valueClock24 = value === null || value === void 0 ? void 0 : value.hour;
if (format === '24' && valueClock > 11)
lowerPointer = true;
if (valueClock > 12)
valueClock -= 12;
valuePosition = (100 / 12) * valueClock;
// Labels
if (format === '12')
labels = (0, utils_1.unique)([
// 12 hours
...(Array.from({ length: 12 }).map((item, index) => ({
value: index === 0 ? 12 : index,
padding: theme.methods.space.value(2.5, 'px'),
style: {
fontSize: 14,
opacity: valid(inputToValue(index === 0 ? 12 : index, 'hour'), 'hour') ? 1 : 0.27,
fill: ((valueClock === 12 && index === 0) || (valueClock === index)) ? colors.inverse : colors.regular
},
position: index * (100 / 12)
})))
], 'position');
else {
labels = [
(0, utils_1.unique)([
// 0-11 hours
...(Array.from({ length: 12 }).map((item, index) => ({
value: index === 0 ? '00' : index,
padding: theme.methods.space.value(2.5, 'px'),
style: {
fontSize: 14,
opacity: valid(inputToValue(index === 0 ? 0 : index, 'hour'), 'hour') ? 1 : 0.27,
fill: valueClock24 === index ? colors.inverse : colors.regular
},
position: index * (100 / 12)
})))
], 'position'),
(0, utils_1.unique)([
// 12-23 hours
...(Array.from({ length: 12 }).map((item, index) => ({
value: 12 + index,
padding: theme.methods.space.value(6, 'px'),
style: {
fontSize: 14,
opacity: valid(inputToValue(12 + index, 'hour'), 'hour') ? 1 : 0.27,
fill: valueClock24 === (12 + index) ? colors.inverse : colors.regular
},
position: index * (100 / 12)
})))
], 'position')
];
}
}
if (selecting === 'minute') {
// Value
valueClock = value === null || value === void 0 ? void 0 : value.minute;
valuePosition = (100 / 60) * valueClock;
// Labels
labels = (0, utils_1.unique)([
// 59 minutes
...(Array.from({ length: 12 }).map((item, index) => ({
value: index === 0 ? '00' : (0, utils_1.getLeadingZerosNumber)((60 / 12) * index),
padding: theme.methods.space.value(2.5, 'px'),
style: {
fontSize: 14,
opacity: valid(inputToValue(index === 0 ? 0 : (60 / 12) * index), 'minute') ? 1 : 0.27,
fill: (valueClock === ((60 / 12) * index)) ? colors.inverse : colors.regular
},
position: index * (100 / 12)
})))
], 'position');
}
if (selecting === 'second') {
// Value
valueClock = value === null || value === void 0 ? void 0 : value.second;
valuePosition = (100 / 60) * valueClock;
// Labels
labels = (0, utils_1.unique)([
// 59 seconds
...(Array.from({ length: 12 }).map((item, index) => ({
value: index === 0 ? '00' : (0, utils_1.getLeadingZerosNumber)((60 / 12) * index),
padding: theme.methods.space.value(2.5, 'px'),
style: {
fontSize: 14,
opacity: valid(inputToValue(index === 0 ? 0 : (60 / 12) * index, 'second'), 'second') ? 1 : 0.27,
fill: (valueClock === ((60 / 12) * index)) ? colors.inverse : colors.regular
},
position: index * (100 / 12)
})))
], 'position');
}
return ((0, jsx_runtime_1.jsxs)(RoundMeter, Object.assign({ ref: (item) => {
if (ref) {
if ((0, utils_1.is)('function', ref))
ref(item);
else
ref.current = item;
}
refs.root.current = item;
}, tonal: tonal, color: color, size: size, labels: labels, arcsVisible: false, childrenPosition: 'pre-marks', onClick: onClick, background: true, BackgroundProps: {
fill: theme.methods.palette.color.value(undefined, 70, true, palette),
onMouseDown: onMouseDown,
onTouchStart: onMouseDown
}, renderLabel: (0, utils_1.is)('function', renderValue) ? (x, y, valueItem, otherProps) => renderValue(value, selecting, x, y, valueItem, otherProps) : undefined }, other, { className: (0, style_react_1.classNames)([
(0, utils_2.staticClassName)('Clock', theme) && [
'amaui-Clock-round-meter'
],
className,
classes.root,
mouseDown && classes.mouseDown
]) }, { children: [(0, jsx_runtime_1.jsx)(Path, { ref: refs.middle, Component: 'circle', r: '4', cx: '120', cy: '120', style: {
stroke: 'none',
fill: palette[40]
} }), (0, jsx_runtime_1.jsx)(Path, { d: 'M 120 119 L 195 119 A 1 1 0 0 1 195 121 L 120 121 A 1 1 0 0 1 121 119', value: valuePosition, style: {
transformOrigin: '50% 50%',
fill: palette[40],
stroke: 'none'
} }), (0, jsx_runtime_1.jsx)(Path, { Component: 'circle', r: '24', cx: lowerPointer ? 182 : 212.5, cy: '120', value: valuePosition, style: {
transformOrigin: 'center',
fill: palette[40],
stroke: 'none'
} })] })));
});
Clock.displayName = 'amaui-Clock';
exports.default = Clock;
;