antonfisher / react-simple-timefield Goto Github PK
View Code? Open in Web Editor NEWSimple React time input field
Home Page: https://antonfisher.com/react-simple-timefield/
License: MIT License
Simple React time input field
Home Page: https://antonfisher.com/react-simple-timefield/
License: MIT License
If I set undefined
to value prop it throws an error Cannot read property 'split' of undefined
. Should be able to have an empty value as a standard <input type="time" />
element.
im using react-hook-form, i want to use ref and register the input, is there any way to do it ?
Thanks for making this awesome library Anton!
I was going to fork this repo and add the feature myself, but it should only take a couple minutes to add a boolean disabled prop and it would be helpful for many users I'm sure! We can't disable the button with pure CSS, and given that this is React we also don't want to circumvent best practices and directly modify the DOM.
Help needed: update ./demo
to React 18 -- please make sure tests and builds are working.
Hello.
I was going to use the package on a project for the user to input a time in minutes + seconds and noticed that there's no prop to define that behaviour.
We can show or not seconds but not hours.
I'm going to edit the code and integrate in my project but I think you might want to consider it.
Thank you for your work.
Hi @antonfisher, how would you (or would you) recommend customizing the hour/minute separator colon? I'm making a timer and I wanted to toggle the hour/minute colon as a second indicator.
Hi, I'm using your package in a TypeScript app, but I noticed that the package doesn't have any declarations.
I'm open to making a declaration file, but one thought I figured I might float by you is to just convert the code to TypeScript. The package is mostly one file that I can probably convert without too much trouble. That way the declarations will stay up to date even with code changes.
If you have some particular reason or preference for not using TypeScript, then that's perfectly fine too :D
Thanks,
Nicholas
Clicking on hours or minutes does not set a cursor position to there. So we need to clear minutes and then we can change the hours:(
Please add argument event
in handle onChange
give the option to change the input border-color on focus event
Hi,
Thanks a lot for your plugin. Amazing work ! 🥇
But, I noticed that you used "text" for your HTML input type. That's not user friendly for mobile users.
Instead, what about to use "number" type by default ?
As of the latest release, line 36 in the dist/index.js
file now contains:
import React from 'react';
This breaks a lot of projects using for example Jest, where items in node_modules are by default not passed through Babel.
People using Jest can add the following to their configs as a temporary workaround, but it's not optimal:
transformIgnorePatterns: ['node_modules/(?!react-simple-timefield)']
It would be great if this allowed for using steps as valid minute input. Eg. 15 = 0, 15, 30, 45
Please add property 'inputRef' for native DOM element input
When I try to add the library I get the following error:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR! peerOptional react@"^16.8.0 || ^17.0.0 || ^18.0.0" from @apollo/[email protected]
npm ERR! node_modules/@apollo/client
npm ERR! dev @apollo/client@"^3.7.0" from the root project
npm ERR! peer @apollo/client@"3.x" from [email protected]
npm ERR! node_modules/aws-appsync-auth-link
npm ERR! dev aws-appsync-auth-link@"^3.0.7" from the root project
npm ERR! 27 more (@emotion/react, ...)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.0.0 || ^17" from [email protected]
npm ERR! node_modules/react-simple-timefield
npm ERR! react-simple-timefield@"^3.2.5" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/react
npm ERR! peer react@"^16.0.0 || ^17" from [email protected]
npm ERR! node_modules/react-simple-timefield
npm ERR! react-simple-timefield@"^3.2.5" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /home/circleci/.npm/eresolve-report.txt for a full report.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/circleci/.npm/_logs/2023-01-20T18_34_43_233Z-debug-0.log
Exited with code exit status 1
Here expample of my code, where i try to add placeholder, but it's not working.
Maybe you can explain how is possible to add placeholder?
<TimeField
value={time !== '00:00' ? time : undefined}
onChange={onChangeTime}
input={
<InputBase
classes={{ root: classes.inputBase }}
className={classes.inlineInput}
placeholder={"HH:MM"}
onFocus={focus}
/>
}
/>
Use modern babel versions, upgrade all babel-*
dependencies to @babel/*
ones.
Hello @antonfisher, I have the following warning in my console due to React component lifecycles changes.
Is it possible to allow user to type 24:00 inside input component?
Hello.
Thank you for creating this and sharing it with others. I found a small bug I would like to report.
If you select part of the input text and type any character, it will allow it.
Best regards,
Paweł
Hi @antonfisher ,
It would also be possible to add milliseconds according to the specifications used in the subtitles field, and use the dot as a placeholder.
Esempio:
00:01:00.000 -> hh:mm:ss:milliseconds
-> milliseconds range [000, 999]
Link: codesandbox
import React, { ChangeEvent, CSSProperties, ReactElement } from "react";
const DEFAULT_COLON = ":";
const DEFAULT_COLON_FULL = ".";
const DEFAULT_VALUE_SHORT = `00${DEFAULT_COLON}00`;
const DEFAULT_VALUE_SECOND = `00${DEFAULT_COLON}00${DEFAULT_COLON}00`;
const DEFAULT_VALUE_FULL = `00${DEFAULT_COLON}00${DEFAULT_COLON}00${DEFAULT_COLON_FULL}000`;
export function isNumber<T>(value: T): boolean {
const number = Number(value);
return !isNaN(number) && String(value) === String(number);
}
export function formatTimeItem(value?: string | number): string {
return `${value || ""}00`.substr(0, 2);
}
export function formatTimeFullItem(value?: string | number): string {
return `${value || ""}000`.substr(0, 3);
}
export function validateTimeAndCursor(
showSeconds = false,
showFull = false,
value = "",
defaultValue = "",
colon = DEFAULT_COLON,
cursorPosition = 0
): [string, number] {
let newCursorPosition = Number(cursorPosition);
let [oldH, oldM, oldTempS] = defaultValue.split(colon);
let [oldS, oldF] = oldTempS.split(DEFAULT_COLON_FULL);
let [newH, newM, newTempS] = String(value).split(colon);
let [newS, newF] = newTempS.split(DEFAULT_COLON_FULL);
//console.log(oldH, oldM, oldS, oldF, oldTempS);
//console.log(newH, newM, newS, newF, newTempS);
newH = formatTimeItem(newH);
if (Number(newH[0]) > 2) {
newH = oldH;
newCursorPosition -= 1;
} else if (Number(newH[0]) === 2) {
if (Number(oldH[0]) === 2 && Number(newH[1]) > 3) {
newH = `2${oldH[1]}`;
newCursorPosition -= 2;
} else if (Number(newH[1]) > 3) {
newH = "23";
}
}
newM = formatTimeItem(newM);
if (Number(newM[0]) > 5) {
newM = oldM;
newCursorPosition -= 1;
}
if (showFull || showSeconds) {
newS = formatTimeItem(newS);
if (Number(newS[0]) > 5) {
newS = oldS;
newCursorPosition -= 1;
}
}
if (showFull) newF = formatTimeFullItem(newF);
const validatedValue = showFull
? `${newH}${colon}${newM}${colon}${newS}${DEFAULT_COLON_FULL}${newF}`
: showSeconds
? `${newH}${colon}${newM}${colon}${newS}`
: `${newH}${colon}${newM}`;
return [validatedValue, newCursorPosition];
}
type onChangeType = (
event: ChangeEvent<HTMLInputElement>,
value: string
) => void;
interface Props {
value?: string;
onChange?: onChangeType;
showSeconds?: boolean;
showFull?: boolean;
input: ReactElement | null;
inputRef?: () => HTMLInputElement | null;
colon?: string;
style?: CSSProperties | {};
}
interface State {
value: string;
_colon: string;
_defaultValue: string;
_showSeconds: boolean;
_showFull: boolean;
_maxLength: number;
}
export default class TimeField extends React.Component<Props, State> {
static defaultProps: Props = {
showSeconds: false,
showFull: false,
input: null,
style: {},
colon: DEFAULT_COLON
};
constructor(props: Props) {
super(props);
const _showSeconds = Boolean(props.showSeconds);
const _showFull = Boolean(props.showFull);
const _defaultValue = _showFull
? DEFAULT_VALUE_FULL
: _showSeconds
? DEFAULT_VALUE_SECOND
: DEFAULT_VALUE_SHORT;
const _colon =
props.colon && props.colon.length === 1 ? props.colon : DEFAULT_COLON;
const [validatedTime] = validateTimeAndCursor(
_showSeconds,
_showFull,
this.props.value,
_defaultValue,
_colon
);
this.state = {
value: validatedTime,
_colon,
_showSeconds,
_showFull,
_defaultValue,
_maxLength: _defaultValue.length
};
this.onInputChange = this.onInputChange.bind(this);
}
componentDidUpdate(prevProps: Props): void {
if (this.props.value !== prevProps.value) {
const [validatedTime] = validateTimeAndCursor(
this.state._showSeconds,
this.state._showFull,
this.props.value,
this.state._defaultValue,
this.state._colon
);
this.setState({
value: validatedTime
});
}
}
onInputChange(
event: ChangeEvent<HTMLInputElement>,
callback: onChangeType
): void {
const oldValue = this.state.value;
const inputEl = event.target;
const inputValue = inputEl.value;
const position = inputEl.selectionEnd || 0;
const isTyped = inputValue.length > oldValue.length;
const cursorCharacter = inputValue[position - 1];
const addedCharacter = isTyped ? cursorCharacter : null;
const removedCharacter = isTyped ? null : oldValue[position];
const replacedSingleCharacter =
inputValue.length === oldValue.length ? oldValue[position - 1] : null;
const colon = this.state._colon;
let newValue = oldValue;
let newPosition = position;
console.log(
newValue,
newPosition,
addedCharacter,
position,
this.state._maxLength,
replacedSingleCharacter
);
if (addedCharacter !== null) {
if (position > this.state._maxLength) {
newPosition = this.state._maxLength;
} else if (
(position === 3 || position === 6) &&
addedCharacter === colon
) {
newValue = `${inputValue.substr(
0,
position - 1
)}${colon}${inputValue.substr(position + 1)}`;
} else if (position === 9 && addedCharacter === DEFAULT_COLON_FULL) {
newValue = `${inputValue.substr(
0,
position - 1
)}${DEFAULT_COLON_FULL}${inputValue.substr(position + 1)}`;
} else if (
(position === 3 || position === 6) &&
isNumber(addedCharacter)
) {
newValue = `${inputValue.substr(
0,
position - 1
)}${colon}${addedCharacter}${inputValue.substr(position + 2)}`;
newPosition = position + 1;
} else if (position === 9 && isNumber(addedCharacter)) {
newValue = `${inputValue.substr(
0,
position - 1
)}${DEFAULT_COLON_FULL}${addedCharacter}${inputValue.substr(
position + 2
)}`;
newPosition = position + 1;
} else if (isNumber(addedCharacter)) {
// user typed a number
newValue =
inputValue.substr(0, position - 1) +
addedCharacter +
inputValue.substr(position + 1);
if (position === 2 || position === 5 || position === 8) {
newPosition = position + 1;
}
} else {
// if user typed NOT a number, then keep old value & position
newPosition = position - 1;
}
} else if (replacedSingleCharacter !== null) {
// user replaced only a single character
if (isNumber(cursorCharacter)) {
if (position - 1 === 2 || position - 1 === 5) {
newValue = `${inputValue.substr(
0,
position - 1
)}${colon}${inputValue.substr(position)}`;
} else if (position - 1 === 8) {
newValue = `${inputValue.substr(
0,
position - 1
)}${DEFAULT_COLON_FULL}${inputValue.substr(position)}`;
} else {
newValue = inputValue;
}
} else {
// user replaced a number on some non-number character
newValue = oldValue;
newPosition = position - 1;
}
} else if (
typeof cursorCharacter !== "undefined" &&
cursorCharacter !== colon &&
!isNumber(cursorCharacter)
) {
// set of characters replaced by non-number
newValue = oldValue;
newPosition = position - 1;
} else if (removedCharacter !== null) {
if ((position === 2 || position === 5) && removedCharacter === colon) {
newValue = `${inputValue.substr(
0,
position - 1
)}0${colon}${inputValue.substr(position)}`;
newPosition = position - 1;
} else if (position === 8 && removedCharacter === DEFAULT_COLON_FULL) {
newValue = `${inputValue.substr(
0,
position - 1
)}0${DEFAULT_COLON_FULL}${inputValue.substr(position)}`;
newPosition = position - 1;
} else {
// user removed a number
newValue = `${inputValue.substr(0, position)}0${inputValue.substr(
position
)}`;
}
}
const [validatedTime, validatedCursorPosition] = validateTimeAndCursor(
this.state._showSeconds,
this.state._showFull,
newValue,
oldValue,
colon,
newPosition
);
this.setState({ value: validatedTime }, () => {
inputEl.selectionStart = validatedCursorPosition;
inputEl.selectionEnd = validatedCursorPosition;
callback(event, validatedTime);
});
event.persist();
}
render(): ReactElement {
const { value } = this.state;
const {
onChange,
style,
showSeconds,
showFull,
input,
inputRef,
colon,
...props
} = this.props; //eslint-disable-line no-unused-vars
const onChangeHandler = (event: ChangeEvent<HTMLInputElement>) =>
this.onInputChange(
event,
(e: ChangeEvent<HTMLInputElement>, v: string) =>
onChange && onChange(e, v)
);
if (input) {
return React.cloneElement(input, {
...props,
value,
style,
onChange: onChangeHandler
});
}
return (
<input
type="text"
{...props}
ref={inputRef}
value={value}
onChange={onChangeHandler}
style={{ width: showFull ? 250 : showSeconds ? 54 : 35, ...style }}
/>
);
}
}
Repro:
It would be nice to support 12 hr format as well as 24hr format.
I've got a case where the time field is required. Having it default to 00:00
makes it odd because they could miss this field completely and have an undesired 00:00
time.
An allowBlank
prop would be useful in this scenario. Visually it could even show --:--
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.