@maggioli-design-system/mds-input-otp 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/app-globals-3a1e7e63.js +5 -0
- package/dist/cjs/index-3ce43d21.js +1360 -0
- package/dist/cjs/index.cjs.js +2 -0
- package/dist/cjs/loader.cjs.js +15 -0
- package/dist/cjs/mds-input-otp.cjs.entry.js +101 -0
- package/dist/cjs/mds-input-otp.cjs.js +25 -0
- package/dist/collection/collection-manifest.json +12 -0
- package/dist/collection/common/aria.js +45 -0
- package/dist/collection/common/browser.js +7 -0
- package/dist/collection/common/date.js +13 -0
- package/dist/collection/common/device.js +6 -0
- package/dist/collection/common/file.js +48 -0
- package/dist/collection/common/floating-controller.js +201 -0
- package/dist/collection/common/icon.js +15 -0
- package/dist/collection/common/keyboard-manager.js +46 -0
- package/dist/collection/common/locale.js +66 -0
- package/dist/collection/common/slot.js +28 -0
- package/dist/collection/common/string.js +30 -0
- package/dist/collection/common/unit.js +22 -0
- package/dist/collection/common/yugop/core.js +16 -0
- package/dist/collection/common/yugop/index.js +3 -0
- package/dist/collection/common/yugop/random-text.js +59 -0
- package/dist/collection/common/yugop/utils/math.js +11 -0
- package/dist/collection/common/yugop/utils/noop.js +1 -0
- package/dist/collection/common/yugop/utils/prng.js +21 -0
- package/dist/collection/common/yugop/utils/string.js +2 -0
- package/dist/collection/components/mds-input-otp/mds-input-otp.css +16 -0
- package/dist/collection/components/mds-input-otp/mds-input-otp.js +159 -0
- package/dist/collection/components/mds-input-otp/test/mds-input-otp.stories.js +56 -0
- package/dist/collection/dictionary/animation.js +5 -0
- package/dist/collection/dictionary/autocomplete.js +59 -0
- package/dist/collection/dictionary/button.js +39 -0
- package/dist/collection/dictionary/color.js +19 -0
- package/dist/collection/dictionary/file-extensions.js +69 -0
- package/dist/collection/dictionary/floating-ui.js +19 -0
- package/dist/collection/dictionary/icon.js +10 -0
- package/dist/collection/dictionary/input.js +37 -0
- package/dist/collection/dictionary/keyboard.js +84 -0
- package/dist/collection/dictionary/loading.js +5 -0
- package/dist/collection/dictionary/text.js +65 -0
- package/dist/collection/dictionary/tree.js +13 -0
- package/dist/collection/dictionary/typography.js +67 -0
- package/dist/collection/dictionary/variant.js +116 -0
- package/dist/collection/fixtures/cities.js +110 -0
- package/dist/collection/fixtures/filenames.js +118 -0
- package/dist/collection/type/animation.js +1 -0
- package/dist/collection/type/autocomplete.js +1 -0
- package/dist/collection/type/button.js +1 -0
- package/dist/collection/type/date.js +1 -0
- package/dist/collection/type/file-types.js +1 -0
- package/dist/collection/type/floating-ui.js +1 -0
- package/dist/collection/type/form-rel.js +1 -0
- package/dist/collection/type/header-bar.js +1 -0
- package/dist/collection/type/input-tip.js +1 -0
- package/dist/collection/type/input.js +1 -0
- package/dist/collection/type/keyboard.js +1 -0
- package/dist/collection/type/loading.js +1 -0
- package/dist/collection/type/preference.js +1 -0
- package/dist/collection/type/text.js +1 -0
- package/dist/collection/type/tree.js +1 -0
- package/dist/collection/type/typography.js +1 -0
- package/dist/collection/type/variant-file-format.js +91 -0
- package/dist/collection/type/variant.js +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.js +1 -0
- package/dist/components/mds-input-otp.d.ts +11 -0
- package/dist/components/mds-input-otp.js +113 -0
- package/dist/documentation.d.ts +443 -0
- package/dist/documentation.json +105 -0
- package/dist/esm/app-globals-0f993ce5.js +3 -0
- package/dist/esm/index-7080a0cd.js +1332 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/mds-input-otp.entry.js +97 -0
- package/dist/esm/mds-input-otp.js +20 -0
- package/dist/esm/polyfills/core-js.js +11 -0
- package/dist/esm/polyfills/dom.js +79 -0
- package/dist/esm/polyfills/es5-html-element.js +1 -0
- package/dist/esm/polyfills/index.js +34 -0
- package/dist/esm/polyfills/system.js +6 -0
- package/dist/esm-es5/app-globals-0f993ce5.js +1 -0
- package/dist/esm-es5/index-7080a0cd.js +1 -0
- package/dist/esm-es5/index.js +0 -0
- package/dist/esm-es5/loader.js +1 -0
- package/dist/esm-es5/mds-input-otp.entry.js +1 -0
- package/dist/esm-es5/mds-input-otp.js +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/mds-input-otp/index.esm.js +0 -0
- package/dist/mds-input-otp/mds-input-otp.esm.js +1 -0
- package/dist/mds-input-otp/mds-input-otp.js +127 -0
- package/dist/mds-input-otp/p-0b4b7d22.system.js +1 -0
- package/dist/mds-input-otp/p-50ea2036.system.js +1 -0
- package/dist/mds-input-otp/p-56ba5cbf.system.js +1 -0
- package/dist/mds-input-otp/p-6fa28cad.entry.js +1 -0
- package/dist/mds-input-otp/p-8d749fc9.system.entry.js +1 -0
- package/dist/mds-input-otp/p-9c3d33cc.system.js +2 -0
- package/dist/mds-input-otp/p-dea9d1b9.js +2 -0
- package/dist/mds-input-otp/p-e1255160.js +1 -0
- package/dist/stats.json +588 -0
- package/dist/types/common/aria.d.ts +7 -0
- package/dist/types/common/browser.d.ts +2 -0
- package/dist/types/common/date.d.ts +4 -0
- package/dist/types/common/device.d.ts +2 -0
- package/dist/types/common/file.d.ts +11 -0
- package/dist/types/common/floating-controller.d.ts +47 -0
- package/dist/types/common/icon.d.ts +5 -0
- package/dist/types/common/keyboard-manager.d.ts +12 -0
- package/dist/types/common/locale.d.ts +20 -0
- package/dist/types/common/slot.d.ts +4 -0
- package/dist/types/common/string.d.ts +4 -0
- package/dist/types/common/unit.d.ts +3 -0
- package/dist/types/common/yugop/core.d.ts +10 -0
- package/dist/types/common/yugop/index.d.ts +1 -0
- package/dist/types/common/yugop/random-text.d.ts +31 -0
- package/dist/types/common/yugop/utils/math.d.ts +3 -0
- package/dist/types/common/yugop/utils/noop.d.ts +1 -0
- package/dist/types/common/yugop/utils/prng.d.ts +8 -0
- package/dist/types/common/yugop/utils/string.d.ts +1 -0
- package/dist/types/components/mds-input-otp/mds-input-otp.d.ts +27 -0
- package/dist/types/components/mds-input-otp/test/mds-input-otp.stories.d.ts +21 -0
- package/dist/types/components.d.ts +61 -0
- package/dist/types/dictionary/animation.d.ts +2 -0
- package/dist/types/dictionary/autocomplete.d.ts +2 -0
- package/dist/types/dictionary/button.d.ts +7 -0
- package/dist/types/dictionary/color.d.ts +3 -0
- package/dist/types/dictionary/file-extensions.d.ts +12 -0
- package/dist/types/dictionary/floating-ui.d.ts +3 -0
- package/dist/types/dictionary/icon.d.ts +4 -0
- package/dist/types/dictionary/input.d.ts +5 -0
- package/dist/types/dictionary/keyboard.d.ts +2 -0
- package/dist/types/dictionary/loading.d.ts +2 -0
- package/dist/types/dictionary/text.d.ts +4 -0
- package/dist/types/dictionary/tree.d.ts +4 -0
- package/dist/types/dictionary/typography.d.ts +11 -0
- package/dist/types/dictionary/variant.d.ts +13 -0
- package/dist/types/fixtures/cities.d.ts +2 -0
- package/dist/types/fixtures/filenames.d.ts +63 -0
- package/dist/types/stencil-public-runtime.d.ts +1680 -0
- package/dist/types/type/animation.d.ts +1 -0
- package/dist/types/type/autocomplete.d.ts +2 -0
- package/dist/types/type/button.d.ts +5 -0
- package/dist/types/type/date.d.ts +5 -0
- package/dist/types/type/file-types.d.ts +1 -0
- package/dist/types/type/floating-ui.d.ts +2 -0
- package/dist/types/type/form-rel.d.ts +1 -0
- package/dist/types/type/header-bar.d.ts +2 -0
- package/dist/types/type/input-tip.d.ts +1 -0
- package/dist/types/type/input.d.ts +7 -0
- package/dist/types/type/keyboard.d.ts +12 -0
- package/dist/types/type/loading.d.ts +1 -0
- package/dist/types/type/preference.d.ts +2 -0
- package/dist/types/type/text.d.ts +3 -0
- package/dist/types/type/tree.d.ts +3 -0
- package/dist/types/type/typography.d.ts +10 -0
- package/dist/types/type/variant-file-format.d.ts +11 -0
- package/dist/types/type/variant.d.ts +13 -0
- package/documentation.json +703 -0
- package/loader/cdn.js +2 -0
- package/loader/index.cjs.js +2 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +2 -0
- package/loader/index.js +3 -0
- package/loader/package.json +11 -0
- package/package.json +54 -0
- package/readme.md +41 -0
- package/src/common/aria.ts +59 -0
- package/src/common/browser.ts +10 -0
- package/src/common/date.ts +21 -0
- package/src/common/device.ts +9 -0
- package/src/common/file.ts +62 -0
- package/src/common/floating-controller.ts +286 -0
- package/src/common/icon.ts +25 -0
- package/src/common/keyboard-manager.ts +51 -0
- package/src/common/locale.ts +90 -0
- package/src/common/slot.ts +35 -0
- package/src/common/string.ts +42 -0
- package/src/common/unit.ts +33 -0
- package/src/common/yugop/core.ts +47 -0
- package/src/common/yugop/index.ts +4 -0
- package/src/common/yugop/random-text.ts +95 -0
- package/src/common/yugop/utils/math.ts +21 -0
- package/src/common/yugop/utils/noop.ts +1 -0
- package/src/common/yugop/utils/prng.ts +35 -0
- package/src/common/yugop/utils/string.ts +4 -0
- package/src/components/mds-input-otp/.gitlab-ci.yml +20 -0
- package/src/components/mds-input-otp/mds-input-otp.css +16 -0
- package/src/components/mds-input-otp/mds-input-otp.tsx +123 -0
- package/src/components/mds-input-otp/readme.md +19 -0
- package/src/components/mds-input-otp/test/mds-input-otp.e2e.ts +11 -0
- package/src/components/mds-input-otp/test/mds-input-otp.stories.tsx +90 -0
- package/src/components.d.ts +61 -0
- package/src/dictionary/animation.ts +8 -0
- package/src/dictionary/autocomplete.ts +62 -0
- package/src/dictionary/button.ts +52 -0
- package/src/dictionary/color.ts +24 -0
- package/src/dictionary/file-extensions.ts +88 -0
- package/src/dictionary/floating-ui.ts +25 -0
- package/src/dictionary/icon.ts +15 -0
- package/src/dictionary/input.ts +48 -0
- package/src/dictionary/keyboard.ts +87 -0
- package/src/dictionary/loading.ts +9 -0
- package/src/dictionary/text.ts +73 -0
- package/src/dictionary/tree.ts +21 -0
- package/src/dictionary/typography.ts +88 -0
- package/src/dictionary/variant.ts +141 -0
- package/src/fixtures/cities.ts +116 -0
- package/src/fixtures/filenames.ts +123 -0
- package/src/fixtures/icons.json +447 -0
- package/src/fixtures/iconsauce.json +306 -0
- package/src/meta/file-format/locale.el.json +44 -0
- package/src/meta/file-format/locale.en.json +44 -0
- package/src/meta/file-format/locale.es.json +44 -0
- package/src/meta/file-format/locale.it.json +44 -0
- package/src/meta/keyboard/keys.json +83 -0
- package/src/tailwind/components.css +51 -0
- package/src/tailwind/fouc.css +118 -0
- package/src/tailwind/index.css +4 -0
- package/src/type/animation.ts +3 -0
- package/src/type/autocomplete.ts +68 -0
- package/src/type/button.ts +32 -0
- package/src/type/date.ts +10 -0
- package/src/type/file-types.ts +61 -0
- package/src/type/floating-ui.ts +17 -0
- package/src/type/form-rel.ts +11 -0
- package/src/type/header-bar.ts +11 -0
- package/src/type/input-tip.ts +11 -0
- package/src/type/input.ts +33 -0
- package/src/type/keyboard.ts +93 -0
- package/src/type/loading.ts +3 -0
- package/src/type/preference.ts +11 -0
- package/src/type/text.ts +63 -0
- package/src/type/tree.ts +12 -0
- package/src/type/typography.ts +65 -0
- package/src/type/variant-file-format.ts +126 -0
- package/src/type/variant.ts +119 -0
- package/www/build/index.esm.js +0 -0
- package/www/build/mds-input-otp.esm.js +1 -0
- package/www/build/mds-input-otp.js +127 -0
- package/www/build/p-0b4b7d22.system.js +1 -0
- package/www/build/p-50ea2036.system.js +1 -0
- package/www/build/p-56ba5cbf.system.js +1 -0
- package/www/build/p-6fa28cad.entry.js +1 -0
- package/www/build/p-8d749fc9.system.entry.js +1 -0
- package/www/build/p-9c3d33cc.system.js +2 -0
- package/www/build/p-dea9d1b9.js +2 -0
- package/www/build/p-e1255160.js +1 -0
- package/www/host.config.json +15 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { render } from 'mustache'
|
|
2
|
+
|
|
3
|
+
type LocaleConfig = {
|
|
4
|
+
el?: Record<string, string | string[]>
|
|
5
|
+
en: Record<string, string | string[]>
|
|
6
|
+
es?: Record<string, string | string[]>
|
|
7
|
+
it?: Record<string, string | string[]>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class Locale {
|
|
11
|
+
rollbackLanguage: string = 'en'
|
|
12
|
+
language: string
|
|
13
|
+
config: LocaleConfig
|
|
14
|
+
closestElement:HTMLElement
|
|
15
|
+
element: HTMLElement
|
|
16
|
+
|
|
17
|
+
constructor (configData?: LocaleConfig) {
|
|
18
|
+
if (configData) {
|
|
19
|
+
this.set(configData)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set = (configData: LocaleConfig): void => {
|
|
24
|
+
this.config = configData
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
lang = (el: HTMLElement): string => {
|
|
28
|
+
this.element = el
|
|
29
|
+
this.closestElement = this.element.closest('[lang]') as HTMLElement
|
|
30
|
+
|
|
31
|
+
if (this.closestElement) {
|
|
32
|
+
if (this.closestElement.lang) {
|
|
33
|
+
this.language = this.closestElement.lang
|
|
34
|
+
return this.language
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.language = this.rollbackLanguage
|
|
39
|
+
return this.language
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
update = (doc?: Document | ShadowRoot): void => {
|
|
43
|
+
const context = doc ?? this.element.shadowRoot
|
|
44
|
+
context && context.querySelectorAll('*').forEach(el => {
|
|
45
|
+
if (el.tagName.toLowerCase().startsWith('mds-')) {
|
|
46
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
47
|
+
if (el && 'updateLang' in el) {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
(el as any).updateLang()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private pluralize = (tag: string | string[], context: Record<string, string | number | boolean>): string => {
|
|
56
|
+
|
|
57
|
+
const languagePhrase: string | string[] = this.config[this.language] ? this.config[this.language][tag] : this.config[this.rollbackLanguage][tag]
|
|
58
|
+
const phrases: string[] = []
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(languagePhrase)) {
|
|
61
|
+
phrases.push(languagePhrase[0])
|
|
62
|
+
phrases.push(languagePhrase[1])
|
|
63
|
+
} else {
|
|
64
|
+
phrases.push(languagePhrase)
|
|
65
|
+
phrases.push(languagePhrase)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const [ defaultPhrase ] = phrases
|
|
69
|
+
let translatePhrase: string = defaultPhrase
|
|
70
|
+
|
|
71
|
+
const keys = Object.keys(context)
|
|
72
|
+
if (keys.length > 0) {
|
|
73
|
+
const [firstKey] = keys
|
|
74
|
+
if (typeof context[firstKey] === 'number') {
|
|
75
|
+
if (context[firstKey] !== 1) {
|
|
76
|
+
translatePhrase = phrases[1]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return render(translatePhrase, context)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get = (tag: string | string[], context?: Record<string, string | number | boolean>): string => {
|
|
85
|
+
if (context) {
|
|
86
|
+
return this.pluralize(tag, context)
|
|
87
|
+
}
|
|
88
|
+
return this.config[this.language] ? this.config[this.language][tag] : this.config[this.rollbackLanguage][tag]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const hasSlottedElements = (el: HTMLElement, name?: string): boolean => {
|
|
2
|
+
const query = name ? `slot[name="${name}"]` : 'slot:not([name])'
|
|
3
|
+
|
|
4
|
+
const slot: HTMLSlotElement = el.shadowRoot?.querySelector(query) as HTMLSlotElement
|
|
5
|
+
if (slot) {
|
|
6
|
+
return slot.assignedElements({ flatten: true }).length > 0
|
|
7
|
+
}
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const hasSlottedNodes = (el: HTMLElement, name?: string): boolean => {
|
|
12
|
+
const query = name ? `slot[name="${name}"]` : 'slot:not([name])'
|
|
13
|
+
|
|
14
|
+
const slot: HTMLSlotElement = el.shadowRoot?.querySelector(query) as HTMLSlotElement
|
|
15
|
+
if (slot) {
|
|
16
|
+
return slot.assignedNodes().length > 0
|
|
17
|
+
}
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hasSlotted = (el: HTMLElement, name?: string): boolean => {
|
|
22
|
+
const query = name ? `slot[name="${name}"]` : 'slot:not([name])'
|
|
23
|
+
|
|
24
|
+
const slot: HTMLSlotElement = el.shadowRoot?.querySelector(query) as HTMLSlotElement
|
|
25
|
+
if (slot) {
|
|
26
|
+
return slot.assignedNodes().length > 0 || slot.assignedElements().length > 0
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
hasSlottedElements,
|
|
33
|
+
hasSlottedNodes,
|
|
34
|
+
hasSlotted,
|
|
35
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const levenshteinDistance = (a: string, b: string): number => {
|
|
2
|
+
const dp: number[][] = Array.from({ length: a.length + 1 }, (_, i) =>
|
|
3
|
+
// eslint-disable-next-line no-nested-ternary
|
|
4
|
+
Array.from({ length: b.length + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
for (let i = 1; i <= a.length; i++) {
|
|
8
|
+
for (let j = 1; j <= b.length; j++) {
|
|
9
|
+
if (a[i - 1] === b[j - 1]) {
|
|
10
|
+
dp[i][j] = dp[i - 1][j - 1]
|
|
11
|
+
} else {
|
|
12
|
+
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return dp[a.length][b.length]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const closest = (input: string, validCodes: string[]): string => {
|
|
22
|
+
let [closest] = validCodes
|
|
23
|
+
let minDistance = levenshteinDistance(input, closest)
|
|
24
|
+
|
|
25
|
+
for (const code of validCodes) {
|
|
26
|
+
const distance = levenshteinDistance(input, code)
|
|
27
|
+
if (distance < minDistance) {
|
|
28
|
+
minDistance = distance
|
|
29
|
+
closest = code
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return closest
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const firstLetterUppercase = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
closest,
|
|
40
|
+
firstLetterUppercase,
|
|
41
|
+
levenshteinDistance,
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const cssDurationToMilliseconds = (duration: string, defaultValue = 1000): number => {
|
|
2
|
+
|
|
3
|
+
if (duration.includes('ms')) {
|
|
4
|
+
return Number(duration.replace('ms', ''))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (duration.includes('s')) {
|
|
8
|
+
return Number(duration.replace('s', '')) * 1000
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return defaultValue
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const cssSizeToNumber = (size: string, defaultValue = 0): number => {
|
|
15
|
+
if (size.includes('px')) {
|
|
16
|
+
return Number(size.replace('px', ''))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (size.includes('rem')) {
|
|
20
|
+
return Number(size.replace('rem', '')) * 16
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (size.includes('em')) {
|
|
24
|
+
return Number(size.replace('em', '')) * 16
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return defaultValue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
cssDurationToMilliseconds,
|
|
32
|
+
cssSizeToNumber,
|
|
33
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { generator } from './utils/prng'
|
|
2
|
+
import { strToCharCodeArray } from './utils/string'
|
|
3
|
+
import { randomSign, minMaxLooped } from './utils/math'
|
|
4
|
+
const rand = generator()
|
|
5
|
+
|
|
6
|
+
const random: (arg0: number, arg1: number) => () => number = (
|
|
7
|
+
base,
|
|
8
|
+
offset,
|
|
9
|
+
) => () => (base + rand.range(0, offset)) * randomSign()
|
|
10
|
+
|
|
11
|
+
export const generateRandomCharCodeArray: (
|
|
12
|
+
arg0: number,
|
|
13
|
+
arg1: number
|
|
14
|
+
) => (arg0: string) => number[] = (base, offset) => str =>
|
|
15
|
+
strToCharCodeArray(str).map(random(base, offset))
|
|
16
|
+
type Options = {
|
|
17
|
+
str: string
|
|
18
|
+
minCharCode: number
|
|
19
|
+
maxCharCode: number
|
|
20
|
+
placeholderChar: string
|
|
21
|
+
charStep: number
|
|
22
|
+
}
|
|
23
|
+
export const charCodeArrayToString: (
|
|
24
|
+
arg0: Options
|
|
25
|
+
) => (arg0: number[]) => string = ({
|
|
26
|
+
str,
|
|
27
|
+
minCharCode,
|
|
28
|
+
maxCharCode,
|
|
29
|
+
placeholderChar,
|
|
30
|
+
charStep,
|
|
31
|
+
}) => charCodes =>
|
|
32
|
+
charCodes.reduce((acc, item, index) => {
|
|
33
|
+
if (item !== 0) {
|
|
34
|
+
if (Math.abs(item) > charStep) {
|
|
35
|
+
return acc + placeholderChar
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
acc +
|
|
40
|
+
String.fromCharCode(
|
|
41
|
+
minMaxLooped(minCharCode, maxCharCode)(str.charCodeAt(index) + item),
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return acc + str.charAt(index)
|
|
47
|
+
}, '')
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { generateRandomCharCodeArray, charCodeArrayToString } from './core'
|
|
2
|
+
import { noop } from './utils/noop'
|
|
3
|
+
|
|
4
|
+
type Options = {
|
|
5
|
+
str: string
|
|
6
|
+
speed?: number
|
|
7
|
+
placeholderChar?: string
|
|
8
|
+
frameOffset?: number
|
|
9
|
+
charOffset?: number
|
|
10
|
+
charStep?: number
|
|
11
|
+
minCharCode?: number
|
|
12
|
+
maxCharCode?: number
|
|
13
|
+
onProgress?: (arg0: string) => void
|
|
14
|
+
onComplete?: (arg0: string) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class RandomText {
|
|
18
|
+
static defaults: Options = {
|
|
19
|
+
str: '',
|
|
20
|
+
speed: 2,
|
|
21
|
+
placeholderChar: '_',
|
|
22
|
+
frameOffset: 30,
|
|
23
|
+
charOffset: 20,
|
|
24
|
+
charStep: 10,
|
|
25
|
+
minCharCode: 32,
|
|
26
|
+
maxCharCode: 122,
|
|
27
|
+
onProgress: noop,
|
|
28
|
+
onComplete: noop,
|
|
29
|
+
}
|
|
30
|
+
str: string
|
|
31
|
+
speed: number
|
|
32
|
+
placeholderChar: string
|
|
33
|
+
frameOffset: number
|
|
34
|
+
charOffset: number
|
|
35
|
+
charStep: number
|
|
36
|
+
minCharCode: number
|
|
37
|
+
maxCharCode: number
|
|
38
|
+
onProgress: (...args: Array<string>) => string
|
|
39
|
+
onComplete: (...args: Array<string>) => string
|
|
40
|
+
rafId: number // TODO: should be #rafId for private
|
|
41
|
+
|
|
42
|
+
constructor (options: Options) {
|
|
43
|
+
Object.assign(this, { ...RandomText.defaults, ...options })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
start = (): void => {
|
|
47
|
+
const { frameOffset, charOffset, str, speed } = this
|
|
48
|
+
const randoms = generateRandomCharCodeArray(frameOffset, charOffset)(str)
|
|
49
|
+
this.stop()
|
|
50
|
+
this.rafId = requestAnimationFrame(() => {
|
|
51
|
+
this.step(randoms, speed, speed)
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stop (): void {
|
|
56
|
+
cancelAnimationFrame(this.rafId)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
step (randoms: number[], stepCount: number, speed: number): void {
|
|
60
|
+
const {
|
|
61
|
+
str,
|
|
62
|
+
charStep,
|
|
63
|
+
minCharCode,
|
|
64
|
+
maxCharCode,
|
|
65
|
+
placeholderChar,
|
|
66
|
+
onProgress,
|
|
67
|
+
onComplete,
|
|
68
|
+
} = this
|
|
69
|
+
const stepArray = randoms.slice(0, stepCount)
|
|
70
|
+
const steppedArray = stepArray.map(item => {
|
|
71
|
+
if (item > 0) return item - 1
|
|
72
|
+
if (item < 0) return item + 1
|
|
73
|
+
return 0
|
|
74
|
+
})
|
|
75
|
+
const output = charCodeArrayToString({
|
|
76
|
+
str,
|
|
77
|
+
minCharCode,
|
|
78
|
+
maxCharCode,
|
|
79
|
+
placeholderChar,
|
|
80
|
+
charStep,
|
|
81
|
+
})(steppedArray)
|
|
82
|
+
const updatedRandoms = [...steppedArray, ...randoms.slice(stepCount)]
|
|
83
|
+
onProgress(output)
|
|
84
|
+
|
|
85
|
+
if (output !== str) {
|
|
86
|
+
this.rafId = requestAnimationFrame(() => {
|
|
87
|
+
this.step(updatedRandoms, stepCount + speed, speed)
|
|
88
|
+
})
|
|
89
|
+
} else {
|
|
90
|
+
onComplete(output)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default RandomText
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { generator } from './prng'
|
|
2
|
+
|
|
3
|
+
const rand = generator()
|
|
4
|
+
export const randomSign: () => number = () =>
|
|
5
|
+
(Math.round(Math.random()) - 0.5) * 2
|
|
6
|
+
export const generateRandomNumbers: (
|
|
7
|
+
arg0: number
|
|
8
|
+
) => (
|
|
9
|
+
arg0: number
|
|
10
|
+
) => (arg0: number) => number[] = base => charOffset => length =>
|
|
11
|
+
[...Array(length)].map(
|
|
12
|
+
() => (base + rand.range(0, charOffset)) * randomSign(),
|
|
13
|
+
)
|
|
14
|
+
export const minMaxLooped: (
|
|
15
|
+
arg0: number,
|
|
16
|
+
arg1: number
|
|
17
|
+
) => (arg0: number) => number = (min, max) => value => {
|
|
18
|
+
if (value > max) return min + (value - max)
|
|
19
|
+
if (value < min) return max + (value - min)
|
|
20
|
+
return value
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const noop: (...rest: unknown[]) => unknown = () => {}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const int32 = 2147483647
|
|
2
|
+
|
|
3
|
+
const gen: (arg0: number) => number = v => (v * 16807) % int32
|
|
4
|
+
|
|
5
|
+
const randomFloat: (arg0: number) => number = v => gen(v) / int32
|
|
6
|
+
|
|
7
|
+
const randomInt: (arg0: number) => number = v => gen(v)
|
|
8
|
+
|
|
9
|
+
type Generator = (
|
|
10
|
+
_?: number
|
|
11
|
+
) => {
|
|
12
|
+
random: () => number
|
|
13
|
+
randomFloat: () => number
|
|
14
|
+
range: (min: number, max: number) => number
|
|
15
|
+
rangeFloat: (min: number, max: number) => number
|
|
16
|
+
}
|
|
17
|
+
export const generator: Generator = (seed = 1) => {
|
|
18
|
+
let value = seed < 1 ? 1 : seed
|
|
19
|
+
|
|
20
|
+
const next: () => number = () => {
|
|
21
|
+
value = randomInt(value)
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
random: () => next(),
|
|
27
|
+
randomFloat: () => randomFloat(next()),
|
|
28
|
+
range: (min, max) => {
|
|
29
|
+
const minimum = min - 0.4999
|
|
30
|
+
const maximum = max + 0.4999
|
|
31
|
+
return Math.round(minimum + (maximum - minimum) * randomFloat(next()))
|
|
32
|
+
},
|
|
33
|
+
rangeFloat: (min, max) => min + (max - min) * randomFloat(next()),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.base-input-otp:
|
|
2
|
+
variables:
|
|
3
|
+
COMPONENT: mds-input-otp
|
|
4
|
+
|
|
5
|
+
# TEST
|
|
6
|
+
input-otp-publish-test:
|
|
7
|
+
extends: [.base-stencil-publish-test, .base-input-otp]
|
|
8
|
+
dependencies: [base-stencil-build, base-isolate]
|
|
9
|
+
|
|
10
|
+
# PUBLISH
|
|
11
|
+
input-otp-publish:
|
|
12
|
+
extends: [.base-stencil-publish, .base-input-otp]
|
|
13
|
+
dependencies: [base-stencil-build, base-isolate]
|
|
14
|
+
needs: [base-stencil-build, base-isolate, input-otp-publish-test]
|
|
15
|
+
|
|
16
|
+
# INSTALL TEST
|
|
17
|
+
input-otp-install-test:
|
|
18
|
+
extends: [.base-stencil-install-test, .base-input-otp]
|
|
19
|
+
dependencies: [base-stencil-build, base-isolate]
|
|
20
|
+
needs: [base-stencil-build, base-isolate, input-otp-publish]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Component, Element, AttachInternals, Host, h, Prop } from '@stencil/core'
|
|
2
|
+
|
|
3
|
+
export interface MdsInputOtpInterface {
|
|
4
|
+
length?: number
|
|
5
|
+
autosubmit?: boolean
|
|
6
|
+
value?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
tag: 'mds-input-otp',
|
|
11
|
+
styleUrl: 'mds-input-otp.css',
|
|
12
|
+
formAssociated: true,
|
|
13
|
+
shadow: true,
|
|
14
|
+
})
|
|
15
|
+
export class MdsInputOtp {
|
|
16
|
+
|
|
17
|
+
@Element() private element: HTMLMdsInputOtpElement
|
|
18
|
+
@AttachInternals() internals: ElementInternals
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Number of digits in the OTP code
|
|
22
|
+
*/
|
|
23
|
+
@Prop() readonly length: number = 6
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Automatically submits the form when the OTP code is complete
|
|
27
|
+
*/
|
|
28
|
+
@Prop({ reflect: true }) readonly autosubmit: boolean = false
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The current value of the OTP code
|
|
32
|
+
*/
|
|
33
|
+
@Prop({ mutable: true, reflect: true }) value?: string = ''
|
|
34
|
+
|
|
35
|
+
private getOtpCode = (): string => {
|
|
36
|
+
const inputs = Array.from(this.element.shadowRoot!.querySelectorAll('mds-input'))
|
|
37
|
+
const otpCode = inputs.map(input => input.value).join('')
|
|
38
|
+
|
|
39
|
+
return otpCode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private setOtpDigit = (currentInput: HTMLMdsInputElement, digit: string): void => {
|
|
43
|
+
currentInput.value = digit
|
|
44
|
+
|
|
45
|
+
const otpCode = this.getOtpCode()
|
|
46
|
+
this.value = otpCode
|
|
47
|
+
this.internals.setFormValue(otpCode)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private submit = (currentInput: HTMLMdsInputElement): void => {
|
|
51
|
+
const isOtpCompleted = this.getOtpCode().length === this.length
|
|
52
|
+
currentInput.blur()
|
|
53
|
+
|
|
54
|
+
if (this.autosubmit && isOtpCompleted) {
|
|
55
|
+
this.internals.form?.requestSubmit()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private handleKeyDown = (e: KeyboardEvent): void => {
|
|
60
|
+
if (e.ctrlKey) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// e.preventDefault() must be called *outside* the ctrlKey check,
|
|
65
|
+
// otherwise the onPaste event won't be triggered correctly
|
|
66
|
+
e.preventDefault()
|
|
67
|
+
|
|
68
|
+
if (isNaN(Number(e.key))) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const currentInput = e.target as HTMLMdsInputElement
|
|
73
|
+
this.setOtpDigit(currentInput, e.key)
|
|
74
|
+
|
|
75
|
+
const nextInput = currentInput.nextElementSibling as HTMLMdsInputElement
|
|
76
|
+
|
|
77
|
+
if (nextInput) {
|
|
78
|
+
nextInput.setFocus()
|
|
79
|
+
} else {
|
|
80
|
+
this.submit(currentInput)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private handlePaste = (e: ClipboardEvent) => {
|
|
85
|
+
e.preventDefault()
|
|
86
|
+
|
|
87
|
+
const pastedText = e.clipboardData?.getData('text')
|
|
88
|
+
|
|
89
|
+
if (isNaN(Number(pastedText))) {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const digits = pastedText?.split('') ?? []
|
|
94
|
+
let currentInput = e.target as HTMLMdsInputElement
|
|
95
|
+
for (const currentDigit of digits) {
|
|
96
|
+
this.setOtpDigit(currentInput, currentDigit)
|
|
97
|
+
|
|
98
|
+
currentInput = currentInput.nextElementSibling as HTMLMdsInputElement
|
|
99
|
+
|
|
100
|
+
if (currentInput) {
|
|
101
|
+
currentInput.setFocus()
|
|
102
|
+
} else {
|
|
103
|
+
this.submit(currentInput)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
render () {
|
|
110
|
+
return (
|
|
111
|
+
<Host>
|
|
112
|
+
{Array.from({ length: this.length }).map(() => (
|
|
113
|
+
<mds-input
|
|
114
|
+
class="input"
|
|
115
|
+
maxlength={1}
|
|
116
|
+
onKeyDown={this.handleKeyDown}
|
|
117
|
+
onPaste={this.handlePaste}
|
|
118
|
+
></mds-input>
|
|
119
|
+
))}
|
|
120
|
+
</Host>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# mds-input-otp
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
<!-- Auto Generated Below -->
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Properties
|
|
9
|
+
|
|
10
|
+
| Property | Attribute | Description | Type | Default |
|
|
11
|
+
| ------------ | ------------ | ------------------------------------------------------------ | --------------------- | ------- |
|
|
12
|
+
| `autosubmit` | `autosubmit` | Automatically submits the form when the OTP code is complete | `boolean` | `false` |
|
|
13
|
+
| `length` | `length` | Number of digits in the OTP code | `number` | `6` |
|
|
14
|
+
| `value` | `value` | The current value of the OTP code | `string \| undefined` | `''` |
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
----------------------------------------------
|
|
18
|
+
|
|
19
|
+
Built with love @ [Gruppo Maggioli](https://www.maggioli.com) from [R&D Department](https://www.maggioli.com/it-it/chi-siamo/ricerca-sviluppo)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { newE2EPage } from '@stencil/core/testing'
|
|
2
|
+
|
|
3
|
+
describe('mds-input-otp', () => {
|
|
4
|
+
it('renders', async () => {
|
|
5
|
+
const page = await newE2EPage()
|
|
6
|
+
await page.setContent('<mds-input-otp></mds-input-otp>')
|
|
7
|
+
|
|
8
|
+
const element = await page.find('mds-input-otp')
|
|
9
|
+
expect(element).toHaveAttribute('hydrated')
|
|
10
|
+
})
|
|
11
|
+
})
|