11import * as CustomText from "../test/custom-text" ;
22import * as Notifications from "../elements/notifications" ;
33import * as CustomTextState from "../states/custom-text-name" ;
4- import { InputIndicator } from "../elements/input-indicator" ;
5- import { debounce } from "throttle-debounce" ;
64import AnimatedModal , { ShowOptions } from "../utils/animated-modal" ;
5+ import { validateWithIndicator } from "../elements/input-validation" ;
6+ import { z } from "zod" ;
77
8- let indicator : InputIndicator | undefined ;
8+ type IncomingData = {
9+ text : string [ ] ;
10+ } ;
911
1012type State = {
1113 textToSave : string [ ] ;
@@ -15,6 +17,35 @@ const state: State = {
1517 textToSave : [ ] ,
1618} ;
1719
20+ const validatedInput = validateWithIndicator (
21+ $ ( "#saveCustomTextModal .textName" ) [ 0 ] as HTMLInputElement ,
22+ {
23+ debounceDelay : 500 ,
24+ schema : z
25+ . string ( )
26+ . min ( 1 )
27+ . max ( 32 )
28+ . regex ( / ^ [ \w \s - ] + $ / , {
29+ message :
30+ "Name can only contain letters, numbers, spaces, underscores and hyphens" ,
31+ } ) ,
32+ isValid : async ( value ) => {
33+ const checkbox = $ ( "#saveCustomTextModal .isLongText" ) . prop (
34+ "checked"
35+ ) as boolean ;
36+ const names = CustomText . getCustomTextNames ( checkbox ) ;
37+ return ! names . includes ( value ) ? true : "Duplicate name" ;
38+ } ,
39+ callback : ( result ) => {
40+ if ( result . status === "success" ) {
41+ $ ( "#saveCustomTextModal button.save" ) . prop ( "disabled" , false ) ;
42+ } else {
43+ $ ( "#saveCustomTextModal button.save" ) . prop ( "disabled" , true ) ;
44+ }
45+ } ,
46+ }
47+ ) ;
48+
1849export async function show ( options : ShowOptions < IncomingData > ) : Promise < void > {
1950 state . textToSave = [ ] ;
2051 void modal . show ( {
@@ -28,10 +59,6 @@ export async function show(options: ShowOptions<IncomingData>): Promise<void> {
2859 } ) ;
2960}
3061
31- function hide ( ) : void {
32- void modal . hide ( ) ;
33- }
34-
3562function save ( ) : boolean {
3663 const name = $ ( "#saveCustomTextModal .textName" ) . val ( ) as string ;
3764 const checkbox = $ ( "#saveCustomTextModal .isLongText" ) . prop (
@@ -59,69 +86,18 @@ function save(): boolean {
5986 }
6087}
6188
62- function updateIndicatorAndButton ( ) : void {
63- const val = $ ( "#saveCustomTextModal .textName" ) . val ( ) as string ;
64- const checkbox = $ ( "#saveCustomTextModal .isLongText" ) . prop (
65- "checked"
66- ) as boolean ;
67-
68- if ( ! val ) {
69- indicator ?. hide ( ) ;
70- $ ( "#saveCustomTextModal button.save" ) . prop ( "disabled" , true ) ;
71- } else {
72- const names = CustomText . getCustomTextNames ( checkbox ) ;
73- if ( names . includes ( val ) ) {
74- indicator ?. show ( "unavailable" ) ;
75- $ ( "#saveCustomTextModal button.save" ) . prop ( "disabled" , true ) ;
76- } else {
77- indicator ?. show ( "available" ) ;
78- $ ( "#saveCustomTextModal button.save" ) . prop ( "disabled" , false ) ;
79- }
80- }
81- }
82-
83- const updateInputAndButtonDebounced = debounce ( 500 , updateIndicatorAndButton ) ;
84-
8589async function setup ( modalEl : HTMLElement ) : Promise < void > {
86- indicator = new InputIndicator ( $ ( "#saveCustomTextModal .textName" ) , {
87- available : {
88- icon : "fa-check" ,
89- level : 1 ,
90- } ,
91- unavailable : {
92- icon : "fa-times" ,
93- level : - 1 ,
94- } ,
95- loading : {
96- icon : "fa-circle-notch" ,
97- spinIcon : true ,
98- level : 0 ,
99- } ,
100- } ) ;
10190 modalEl . addEventListener ( "submit" , ( e ) => {
10291 e . preventDefault ( ) ;
103- if ( save ( ) ) hide ( ) ;
104- } ) ;
105- modalEl . querySelector ( ".textName" ) ?. addEventListener ( "input" , ( e ) => {
106- const val = ( e . target as HTMLInputElement ) . value ;
107- if ( val . length > 0 ) {
108- indicator ?. show ( "loading" ) ;
109- updateInputAndButtonDebounced ( ) ;
92+ if ( validatedInput . isValid ( ) === true && save ( ) ) {
93+ void modal . hide ( ) ;
11094 }
11195 } ) ;
11296 modalEl . querySelector ( ".isLongText" ) ?. addEventListener ( "input" , ( e ) => {
113- const val = ( e . target as HTMLInputElement ) . value ;
114- if ( val . length > 0 ) {
115- indicator ?. show ( "loading" ) ;
116- updateInputAndButtonDebounced ( ) ;
117- }
97+ validatedInput . triggerValidation ( ) ;
11898 } ) ;
11999}
120100
121- type IncomingData = {
122- text : string [ ] ;
123- } ;
124-
125101const modal = new AnimatedModal < IncomingData > ( {
126102 dialogId : "saveCustomTextModal" ,
127103 setup,
0 commit comments