Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Km.24 hour input exploration #1274

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Working on imaskjs
kiley-mitti committed Jan 15, 2024
commit d260d17d1e81c8e53cba29f3dee8b10f8a31a960
69 changes: 60 additions & 9 deletions packages/web-components/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
@@ -47,11 +47,10 @@
},
"dependencies": {
"@floating-ui/dom": "~1.0.6",
"@maskito/core": "~1.9.0",
"@maskito/kit": "~1.9.0",
"@stencil/core": "~3.4.1",
"date-fns": "~2.21.3",
"date-fns-tz": "~1.3.7"
"date-fns-tz": "~1.3.7",
"imask": "~7.3.0"
},
"license": "MIT",
"devDependencies": {
16 changes: 8 additions & 8 deletions packages/web-components/src/components.d.ts
Original file line number Diff line number Diff line change
@@ -20061,6 +20061,10 @@ export namespace Components {
* The help or explanation text
*/
"helpText"?: string;
/**
* Includes seconds as part of the time field
*/
"includeSeconds": boolean;
/**
* Presentational only. Renders the Input Field as invalid.
*/
@@ -20105,10 +20109,6 @@ export namespace Components {
* The input step attribute
*/
"step"?: string;
/**
* Sets input type of 24h input [Maskito types]()
*/
"timeInput": 'HH:MM' | 'HH:MM:SS' | 'HH:MM:SS.MSS';
/**
* Sets time or datetime types to 12hr/24hr
*/
@@ -55768,6 +55768,10 @@ declare namespace LocalJSX {
* The help or explanation text
*/
"helpText"?: string;
/**
* Includes seconds as part of the time field
*/
"includeSeconds"?: boolean;
/**
* Presentational only. Renders the Input Field as invalid.
*/
@@ -55824,10 +55828,6 @@ declare namespace LocalJSX {
* The input step attribute
*/
"step"?: string;
/**
* Sets input type of 24h input [Maskito types]()
*/
"timeInput"?: 'HH:MM' | 'HH:MM:SS' | 'HH:MM:SS.MSS';
/**
* Sets time or datetime types to 12hr/24hr
*/
Original file line number Diff line number Diff line change
@@ -12,8 +12,7 @@ import {
} from '@stencil/core'
import { FormFieldInterface } from '../../common/interfaces.module'
import { hasSlot, renderHiddenInput } from '../../utils/utils'
import { Maskito } from '@maskito/core'
import { maskitoTimeOptionsGenerator } from '@maskito/kit'
import IMask, { MaskedRange } from 'imask'

let id = 0

@@ -43,17 +42,13 @@ let id = 0
export class RuxTimeInput implements FormFieldInterface {
private inputId = `rux-input-${++id}`
private inputEl!: HTMLInputElement
//this is so we can destroy the maskito masking when the element is removed from dom.
private maskedElement: any = null
private iMaskRef: any | null = null

@Element() el!: HTMLRuxInputElement

@State() hasLabelSlot = false
@State() hasHelpSlot = false
@State() hasErrorSlot = false
//for 24hour time validation we need an interim state to check against
@State() state24 = ''

@State() hasFocus = false

/**
@@ -131,9 +126,9 @@ export class RuxTimeInput implements FormFieldInterface {
@Prop() timeformat: '12h' | '24h' = '12h'

/**
* Sets input type of 24h input [Maskito types]()
* Includes seconds as part of the time field
*/
@Prop() timeInput: 'HH:MM' | 'HH:MM:SS' | 'HH:MM:SS.MSS' = 'HH:MM:SS'
@Prop() includeSeconds: boolean = false

/**
* Fired when the value of the input changes - [HTMLElement/input_event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)
@@ -176,44 +171,88 @@ export class RuxTimeInput implements FormFieldInterface {
this._handleSlotChange()
}

//??TODO Can we use maskito input on 12hr even though it doesn't have an AM/PM?
@Watch('value')
handleValueChange() {
if (this.timeformat !== '24h') return
if (this.value && this.state24 !== this.value) this.state24 = this.value
//!!TODO make value work with masking
}

connectedCallback() {
this._onChange = this._onChange.bind(this)
this._onInput = this._onInput.bind(this)
this._onMod = this._onMod.bind(this)
this._checkValue = this._checkValue.bind(this)
this._handleSlotChange = this._handleSlotChange.bind(this)
if (this.timeformat === '24h') this.state24 = this.value
this._onAccept = this._onAccept.bind(this)
this._onComplete = this._onComplete.bind(this)
}

disconnectedCallback() {
this.el!.shadowRoot!.removeEventListener(
'slotchange',
this._handleSlotChange
)
//remove Maskito when the element is removed from dom
this.maskedElement.destroy()
this.iMaskRef?.destroy()
}

componentWillLoad() {
this._handleSlotChange()
if (this.timeformat === '24h' && this.value) this.state24 = this.value
}

private maskVals = {
overwrite: true,
mask:
this.timeformat === '24h'
? `HH{:}mm${this.includeSeconds ? '{:}ss' : ''}`
: `hh{:}mm${this.includeSeconds ? '{:}ss' : ''} A`,
lazy: false,
autofix: true,
blocks: {
HH: {
mask: MaskedRange,
from: 0,
to: 24,
maxLength: 2,
placeholderChar: 'H',
autofix: 'pad',
},
hh: {
mask: MaskedRange,
from: 0,
to: 12,
maxLength: 2,
placeholderChar: 'h',
autofix: 'pad',
},
mm: {
mask: MaskedRange,
from: 0,
to: 59,
maxLength: 2,
placeholderChar: 'm',
autofix: 'pad',
},
ss: {
mask: MaskedRange,
from: 0,
to: 59,
maxLength: 2,
placeholderChar: 's',
autofix: 'pad',
},
A: {
mask: IMask.MaskedEnum,
placeholderChar: 'a',
prepareChar: (char: string) => char.toUpperCase(),
enum: ['AM', 'PM'],
},
},
}

componentDidLoad() {
//add maskito once the input for it is loaded into the dom
if (this.timeformat === '24h' && this.inputEl) {
//masking options
const inputMaskOptions = maskitoTimeOptionsGenerator({
mode: this.timeInput,
})
//assign it to a variable so we can remove the mask on disconnected callback
this.maskedElement = new Maskito(this.inputEl, inputMaskOptions)
if (this.inputEl) {
//@ts-ignore - it thinks autofix isnt assignable to masked Range and it is wrong.
this.iMaskRef = IMask(this.inputEl, this.maskVals)
this.iMaskRef.on('accept', this._onAccept)
this.iMaskRef.on('complete', this._onComplete)
}
}

@@ -223,15 +262,11 @@ export class RuxTimeInput implements FormFieldInterface {

private _onChange(e: Event) {
const target = e.target as HTMLInputElement
if (this.timeformat === '24h') this._onMod(target.value)
else this.value = target.value
this.ruxChange.emit()
}

private _onInput(e: Event) {
const target = e.target as HTMLInputElement
if (this.timeformat === '24h') this._onMod(target.value)
else this.value = target.value
this.ruxInput.emit()
}

@@ -251,13 +286,35 @@ export class RuxTimeInput implements FormFieldInterface {
this.hasHelpSlot = hasSlot(this.el, 'help-text')
}

private _onMod(inputValue: string) {
this.state24 = inputValue
const timeRegex = /^([0-1]?[0-9]|2[0-4]):([0-5][0-9])(:[0-5][0-9])?(.[0-9]?[0-9]?[0-9])?$/
if (timeRegex.test(inputValue)) this.value = this.state24
private _checkValue() {
if (this.timeformat === '12h') {
//we need to check whether AM or PM have been selected and convert time to military for the value
console.log('check!!', this.value, this.iMaskRef.unmaskedValue)
if (this.iMaskRef.unmaskedValue.includes('A')) {
console.log('this is AM!')
return
}
if (this.iMaskRef.unmaskedValue.includes('P')) {
console.log('This is PM!')
return
}
console.log('this is nothing')
}
const timeRegex = /^([0-1]?[0-9]|2[0-4]):([0-5][0-9])(:[0-5][0-9])?$/
if (timeRegex.test(this.iMaskRef.unmaskedValue))
this.value = this.iMaskRef.unmaskedValue
else this.value = ''
}

private _onAccept(e: InputEvent) {
this._checkValue()
this.ruxChange.emit()
}

private _onComplete(e: CustomEvent) {
console.log('complete')
}

render() {
const {
disabled,
@@ -339,14 +396,9 @@ export class RuxTimeInput implements FormFieldInterface {
name={name}
disabled={disabled}
ref={(el) => (this.inputEl = el!)}
type={this.timeformat === '24h' ? 'text' : 'time'}
type="text"
aria-invalid={invalid ? 'true' : 'false'}
placeholder={placeholder || this.timeInput}
value={
this.timeformat === '24h'
? this.state24
: this.value
}
placeholder={placeholder}
required={required}
class="native-input"
id={this.inputId}
48 changes: 8 additions & 40 deletions packages/web-components/src/index.html
Original file line number Diff line number Diff line change
@@ -31,55 +31,19 @@
</head>

<body>
<section>
<span>
<rux-input
value="2024-01-30T23:28"
type="datetime-local"
id="showval"
label="Datetime Local"
></rux-input>
</span>
<span> <rux-input type="time" label="Time"></rux-input></span>
<span
><rux-input
type="datetime-local"
timeformat="24h"
label="Datetime 24hr"
value="2024-01-30T23:28"
></rux-input
></span>
<span
><rux-input
value="12:3"
type="time"
timeformat="24h"
label="Time 24hr"
></rux-input
></span>
</section>
<section>
<h2 style="margin: 0">Control Group</h2>
<span><input type="time" /><br /></span>
<span><input type="datetime-local" /><br /></span>
</section>
<section>
<rux-input type="date" value="2024-01-11" label="Date"> </rux-input
><rux-input
type="time"
timeformat="24h"
placeholder="--:--:--"
label="Time"
></rux-input>
</section>
<section>
<rux-time-input></rux-time-input>
<rux-time-input timeformat="24h" disabled invalid></rux-time-input>
<rux-time-input label="12 Hour Time"></rux-time-input>
<rux-time-input
label="24 Hour Time"
timeformat="24h"
value="10:24"
time-input="HH:MM:SS.MSS"
include-seconds
></rux-time-input>
<rux-time-input timeformat="24h" disabled invalid></rux-time-input>
</section>

<script>
@@ -104,6 +68,10 @@ <h2 style="margin: 0">Control Group</h2>
})
}
})
document.addEventListener('ruxchange', () =>
console.log('RUXCHANGE')
)
document.addEventListener('ruxinput', () => console.log('RUXINPUT'))
</script>
</body>
</html>