diff --git a/docs/api/input.md b/docs/api/input.md index 6bdecfc9568..55f469ff798 100644 --- a/docs/api/input.md +++ b/docs/api/input.md @@ -120,6 +120,26 @@ import FilteringData from '@site/static/usage/v7/input/filtering/index.md'; +## Input Masking + +Input masks are expressions that constrain input to support valid input values. Ionic recommends using [Maskito](https://tinkoff.github.io/maskito/getting-started/what-is-maskito) for input masking. Maskito is a lightweight, dependency-free library for masking input fields. It supports a wide range of masks, including phone numbers, credit cards, dates, and more. + +To get started with Maskito, install the library: + +```bash +npm install @maskito/core @maskito/{angular,react,vue} +``` + +import Masking from '@site/static/usage/v7/input/mask/index.md'; + + + +:::note + +Please submit bug reports with Maskito to the [Maskito Github repository](https://github.com/Tinkoff/maskito/issues). For technical support, please use the [Ionic Forum](https://forum.ionicframework.com/) or [Ionic Discord](http://chat.ionicframework.com/). + +::: + ## Theming ### Colors diff --git a/src/components/global/Playground/stackblitz.utils.ts b/src/components/global/Playground/stackblitz.utils.ts index ad2947ba15b..f379a2d472c 100644 --- a/src/components/global/Playground/stackblitz.utils.ts +++ b/src/components/global/Playground/stackblitz.utils.ts @@ -183,6 +183,15 @@ const openReactEditor = async (code: string, options?: EditorOptions) => { options.version ); + const package_json = JSON.parse(defaultFiles[4]); + + if (options?.dependencies) { + package_json.dependencies = { + ...package_json.dependencies, + ...options.dependencies, + }; + } + const appTsx = 'src/App.tsx'; const files = { 'public/index.html': defaultFiles[6], @@ -191,7 +200,7 @@ const openReactEditor = async (code: string, options?: EditorOptions) => { 'src/main.tsx': code, 'src/theme/variables.css': defaultFiles[2], 'tsconfig.json': defaultFiles[3], - 'package.json': defaultFiles[4], + 'package.json': JSON.stringify(package_json, null, 2), 'package-lock.json': defaultFiles[5], ...options?.files, ...options?.dependencies, @@ -227,6 +236,15 @@ const openVueEditor = async (code: string, options?: EditorOptions) => { options.version ); + const package_json = JSON.parse(defaultFiles[0]); + + if (options?.dependencies) { + package_json.dependencies = { + ...package_json.dependencies, + ...options.dependencies, + }; + } + const mainTs = 'src/main.ts'; const files = { 'src/App.vue': defaultFiles[6], @@ -236,7 +254,7 @@ const openVueEditor = async (code: string, options?: EditorOptions) => { 'src/theme/variables.css': defaultFiles[3], 'index.html': defaultFiles[2], 'vite.config.ts': defaultFiles[4], - 'package.json': defaultFiles[0], + 'package.json': JSON.stringify(package_json, null, 2), 'package-lock.json': defaultFiles[1], 'tsconfig.json': defaultFiles[7], 'tsconfig.node.json': defaultFiles[8], diff --git a/static/usage/v7/input/mask/angular/app_module_ts.md b/static/usage/v7/input/mask/angular/app_module_ts.md new file mode 100644 index 00000000000..653793d4262 --- /dev/null +++ b/static/usage/v7/input/mask/angular/app_module_ts.md @@ -0,0 +1,19 @@ +```ts +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; + +import { IonicModule } from '@ionic/angular'; + +import { AppComponent } from './app.component'; +import { ExampleComponent } from './example.component'; + +import { MaskitoModule } from '@maskito/angular'; + +@NgModule({ + imports: [BrowserModule, FormsModule, MaskitoModule, IonicModule.forRoot({})], + declarations: [AppComponent, ExampleComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` diff --git a/static/usage/v7/input/mask/angular/example_component_html.md b/static/usage/v7/input/mask/angular/example_component_html.md new file mode 100644 index 00000000000..f6d27b19558 --- /dev/null +++ b/static/usage/v7/input/mask/angular/example_component_html.md @@ -0,0 +1,20 @@ +```html + + + + + + + + +``` diff --git a/static/usage/v7/input/mask/angular/example_component_ts.md b/static/usage/v7/input/mask/angular/example_component_ts.md new file mode 100644 index 00000000000..2294a2e3403 --- /dev/null +++ b/static/usage/v7/input/mask/angular/example_component_ts.md @@ -0,0 +1,31 @@ +```ts +import { Component } from '@angular/core'; + +import { MaskitoOptions, MaskitoElementPredicateAsync } from '@maskito/core'; + +@Component({ + selector: 'app-example', + templateUrl: 'example.component.html', +}) +export class ExampleComponent { + readonly phoneMask: MaskitoOptions = { + mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/], + }; + + readonly cardMask: MaskitoOptions = { + mask: [ + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ], + }; + + readonly maskPredicate: MaskitoElementPredicateAsync = async (el) => (el as HTMLIonInputElement).getInputElement(); +} +``` diff --git a/static/usage/v7/input/mask/demo.html b/static/usage/v7/input/mask/demo.html new file mode 100644 index 00000000000..fc02f355ef7 --- /dev/null +++ b/static/usage/v7/input/mask/demo.html @@ -0,0 +1,73 @@ + + + + + + Input + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+ + + diff --git a/static/usage/v7/input/mask/index.md b/static/usage/v7/input/mask/index.md new file mode 100644 index 00000000000..92ae30b967f --- /dev/null +++ b/static/usage/v7/input/mask/index.md @@ -0,0 +1,58 @@ +import Playground from '@site/src/components/global/Playground'; + +import javascript_index_html from './javascript/index_html.md'; +import javascript_index_ts from './javascript/index_ts.md'; + +import react_main_tsx from './react.md'; + +import vue_example_vue from './vue.md'; + +import angular_app_module_ts from './angular/app_module_ts.md'; +import angular_example_component_html from './angular/example_component_html.md'; +import angular_example_component_ts from './angular/example_component_ts.md'; + + diff --git a/static/usage/v7/input/mask/javascript/index_html.md b/static/usage/v7/input/mask/javascript/index_html.md new file mode 100644 index 00000000000..512a34ff9f5 --- /dev/null +++ b/static/usage/v7/input/mask/javascript/index_html.md @@ -0,0 +1,58 @@ +```html + + + + + + + + + + + + + + + + + + + + + + + +``` diff --git a/static/usage/v7/input/mask/javascript/index_ts.md b/static/usage/v7/input/mask/javascript/index_ts.md new file mode 100644 index 00000000000..89d38873488 --- /dev/null +++ b/static/usage/v7/input/mask/javascript/index_ts.md @@ -0,0 +1,28 @@ +```ts +import { defineCustomElements } from '@ionic/core/loader'; + +import { Maskito } from '@maskito/core'; + +/* Core CSS required for Ionic components to work properly */ +import '@ionic/core/css/core.css'; + +/* Basic CSS for apps built with Ionic */ +import '@ionic/core/css/normalize.css'; +import '@ionic/core/css/structure.css'; +import '@ionic/core/css/typography.css'; + +/* Optional CSS utils that can be commented out */ +import '@ionic/core/css/padding.css'; +import '@ionic/core/css/float-elements.css'; +import '@ionic/core/css/text-alignment.css'; +import '@ionic/core/css/text-transformation.css'; +import '@ionic/core/css/flex-utils.css'; +import '@ionic/core/css/display.css'; + +/* Theme variables */ +import './theme/variables.css'; + +defineCustomElements(); + +(window as any).Maskito = Maskito; +``` diff --git a/static/usage/v7/input/mask/react.md b/static/usage/v7/input/mask/react.md new file mode 100644 index 00000000000..dd22df29914 --- /dev/null +++ b/static/usage/v7/input/mask/react.md @@ -0,0 +1,55 @@ +```tsx +import React from 'react'; +import { IonInput, IonItem, IonList } from '@ionic/react'; +import { useMaskito } from '@maskito/react'; + +function Example() { + const cardMask = useMaskito({ + options: { + mask: [ + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ' ', + ...Array(4).fill(/\d/), + ], + }, + }); + + const phoneMask = useMaskito({ + options: { + mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/], + }, + }); + + return ( + + + { + if (cardRef) { + cardMask(await cardRef.getInputElement()); + } + }} + label="Card number" + placeholder="0000 0000 0000 0000" + > + + + { + if (phoneInput) { + phoneMask(await phoneInput.getInputElement()); + } + }} + label="US phone number" + placeholder="+1 (xxx) xxx-xxxx" + > + + + ); +} +export default Example; +``` diff --git a/static/usage/v7/input/mask/vue.md b/static/usage/v7/input/mask/vue.md new file mode 100644 index 00000000000..40c19079565 --- /dev/null +++ b/static/usage/v7/input/mask/vue.md @@ -0,0 +1,49 @@ +```html + + + +```