UNPKG

@amaui/ui-react

Version:
429 lines (428 loc) 21.7 kB
"use strict"; 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;