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

Masked input #280

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion libraries/ui-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
},
"dependencies": {
"@stencil/core": "^4.12.6",
"@types/resize-observer-browser": "^0.1.5"
"@types/resize-observer-browser": "^0.1.5",
"imask": "^7.6.1"
},
"devDependencies": {
"@fontsource/noto-sans": "^4.5.11",
Expand Down
34 changes: 34 additions & 0 deletions libraries/ui-library/src/components/six-input/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,40 @@ <h2>Events Disclaimer</h2>
});
</script>
</section>

<h2>Masks</h2>

<p>Sometimes inputs need to be masked in order to enforce a certain format.</p>
<p>Use the <code>mask</code> attribute to apply a simple mask.</p>

<six-input mask="000.000,00" help-text='mask="000.000,00"'></six-input>

<p>Use the <code>setMask</code> method to apply more complex masks</p>

<six-input id="complex-mask-input"></six-input>

<script type="module">
const maskInput = document.getElementById('complex-mask-input');
maskInput.setMask({
mask: 'XXX-XX-0000',
definitions: {
X: {
mask: '0',
displayChar: 'X',
placeholderChar: '#',
},
},
lazy: false,
overwrite: 'shift',
});
</script>

<p>For more info on the format of the mask object passed to <code>setMask()</code> and the <code>mask</code> attribute, take a look at <a href="https://imask.js.org/" target="_blank" rel="noreferrer noopener">https://imask.js.org/ <six-icon size="small">launch</six-icon></a></p>

<six-alert type="warning" open>
<six-icon slot="icon">warning</six-icon>
When masking a input, it's <code>value</code> will contain any additional characters added by the mask.
</six-alert>
</div>
</body>
</html>
40 changes: 37 additions & 3 deletions libraries/ui-library/src/components/six-input/six-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { hasSlot } from '../../utils/slot';
import { EmptyPayload } from '../../utils/types';
import { EventListeners } from '../../utils/event-listeners';
import { submitForm } from '../../utils/form';
import IMask, { FactoryArg, InputMask } from 'imask';

const ICON_SIZES: Record<'small' | 'medium' | 'large', 'xSmall' | 'small' | 'medium'> = {
large: 'medium',
Expand Down Expand Up @@ -52,6 +53,8 @@ export class SixInput {
private errorTextId = `input-error-text-${id}`;
private nativeInput?: HTMLInputElement;
private eventListeners = new EventListeners();
private inputMask?: InputMask<FactoryArg>;
private maskOptions?: FactoryArg;

@Element() host!: HTMLSixInputElement;

Expand Down Expand Up @@ -106,6 +109,9 @@ export class SixInput {
/** A pattern to validate input against. */
@Prop({ reflect: true }) pattern?: string;

/** Applies a simple mask to the input element. Use setMask(maskOptions) if more options are needed */
@Prop({ reflect: true, attribute: 'mask' }) textMask?: string;

/**
* Internal: Styles the input for the dropdown filter search.
*/
Expand Down Expand Up @@ -191,13 +197,25 @@ export class SixInput {
this.eventListeners.forward('six-input-blur', 'blur', this.host);
}

componentDidRender() {
if (this.textMask) {
this.maskOptions = { mask: this.textMask };
}
if (!this.inputMask && this.maskOptions && this.type === 'text') {
this.inputMask = IMask(this.nativeInput, this.maskOptions);
}
}

componentWillLoad() {
this.handleSlotChange();
}

disconnectedCallback() {
this.host.shadowRoot?.removeEventListener('slotchange', this.handleSlotChange);
this.eventListeners.removeAll();
if (this.inputMask) {
this.inputMask.destroy();
}
}

/** Sets focus on the input. */
Expand Down Expand Up @@ -247,18 +265,25 @@ export class SixInput {
}
}

/** Applies a mask on the input element. Requires a mask options object as argument, See https://imask.js.org/guide.html#masked for syntax and examples */
@Method()
async setMask(maskOptions: FactoryArg) {
this.maskOptions = maskOptions;
}

private handleChange = (event: Event) => {
event.stopPropagation();
if (this.nativeInput != null) {
this.value = this.nativeInput.value;
this.updateValueWhenHandlingEvent();
this.sixChange.emit();
}
};

private handleInput = (event: Event) => {
event.stopPropagation();
if (this.nativeInput != null) {
this.value = this.nativeInput.value;
this.inputMask?._onInput(event);
this.updateValueWhenHandlingEvent();
this.sixInput.emit();
}
};
Expand All @@ -275,6 +300,7 @@ export class SixInput {

private handleClearClick = (event: MouseEvent) => {
this.value = '';
this.inputMask?.updateValue();
this.sixClear.emit();
this.sixInput.emit();
this.sixChange.emit();
Expand Down Expand Up @@ -309,6 +335,14 @@ export class SixInput {
return (this.value ?? '').toString();
}

private getDisplayValue(): string {
return this.inputMask ? (this.inputMask.displayValue ?? '').toString() : this.getValue();
}

private updateValueWhenHandlingEvent(): void {
this.value = this.inputMask ? this.inputMask.value : this.nativeInput!.value;
}

render() {
return (
<FormControl
Expand Down Expand Up @@ -371,7 +405,7 @@ export class SixInput {
min={this.min}
max={this.max}
step={this.step}
value={this.getValue()}
value={this.getDisplayValue()}
autoCapitalize={this.autocapitalize}
autoComplete={this.autocomplete}
autoCorrect={this.autocorrect}
Expand Down
Loading