github.com/soulteary/pocket-bookcase@v0.0.0-20240428065142-0b5a9a0fc98a/internal/view/assets/js/component/dialog.js (about)

     1  var template = `
     2  <div v-if="visible" class="custom-dialog-overlay" @keyup.esc="handleEscPressed">
     3  	<div class="custom-dialog">
     4  		<p class="custom-dialog-header">{{title}}</p>
     5  		<div class="custom-dialog-body">
     6  			<slot>
     7  				<p class="custom-dialog-content">{{content}}</p>
     8  				<template v-for="(field,index) in formFields">
     9  					<label v-if="showLabel && field.type !== 'check'">{{field.label}} :</label>
    10  					<textarea v-if="field.type === 'area'"
    11  						:style="{gridColumnEnd: showLabel ? null : 'span 2'}" 
    12  						:placeholder="field.label" 
    13  						:tabindex="index+1"
    14  						ref="input"
    15  						v-model="field.value" 
    16  						@focus="$event.target.select()"
    17  						@keyup="handleInput(index)">
    18  					</textarea>
    19  					<label v-else-if="field.type === 'check'" class="checkbox-field">
    20  						<input type="checkbox" 
    21  							v-model="field.value" 
    22  							:tabindex="index+1">{{field.label}}
    23  					</label>
    24  					<input v-else
    25  						:style="{gridColumnEnd: showLabel ? null : 'span 2'}" 
    26  						:type="fieldType(field)" 
    27  						:placeholder="field.label" 
    28  						:tabindex="index+1"
    29  						ref="input"
    30  						v-model="field.value" 
    31  						@focus="$event.target.select()"
    32  						@keyup="handleInput(index)"
    33  						@keyup.enter="handleInputEnter(index)">
    34  					<button :ref="'suggestion-'+index"
    35  						v-if="field.suggestion"
    36  						@click="handleInputEnter(index)"
    37  						class="suggestion">{{field.suggestion}}</button>
    38  				</template>
    39  			</slot>
    40  		</div>
    41  		<div class="custom-dialog-footer">
    42  			<i v-if="loading" class="fas fa-fw fa-spinner fa-spin"></i>
    43  			<slot v-else name="custom-footer">
    44  				<a v-if="secondText" 
    45  					:tabindex="btnTabIndex+1" 
    46  					@click="handleSecondClick" 
    47  					@keyup.enter="handleSecondClick" 
    48  					class="custom-dialog-button">{{secondText}}
    49  				</a>
    50  				<a :tabindex="btnTabIndex" 
    51  					ref="mainButton"
    52  					@click="handleMainClick" 
    53  					@keyup.enter="handleMainClick" 
    54  					class="custom-dialog-button main">{{mainText}}
    55  				</a>
    56  			</slot>
    57  		</div>
    58  	</div>
    59  </div>`;
    60  
    61  export default {
    62  	template: template,
    63  	props: {
    64  		title: String,
    65  		loading: Boolean,
    66  		visible: Boolean,
    67  		content: {
    68  			type: String,
    69  			default: "",
    70  		},
    71  		fields: {
    72  			type: Array,
    73  			default() {
    74  				return [];
    75  			},
    76  		},
    77  		showLabel: {
    78  			type: Boolean,
    79  			default: false,
    80  		},
    81  		mainText: {
    82  			type: String,
    83  			default: "OK",
    84  		},
    85  		secondText: String,
    86  		mainClick: {
    87  			type: Function,
    88  			default() {
    89  				this.visible = false;
    90  			},
    91  		},
    92  		secondClick: {
    93  			type: Function,
    94  			default() {
    95  				this.visible = false;
    96  			},
    97  		},
    98  		escPressed: {
    99  			type: Function,
   100  			default() {
   101  				this.visible = false;
   102  			},
   103  		},
   104  	},
   105  	data() {
   106  		return {
   107  			formFields: [],
   108  		};
   109  	},
   110  	computed: {
   111  		btnTabIndex() {
   112  			return this.fields.length + 1;
   113  		},
   114  	},
   115  	watch: {
   116  		fields: {
   117  			immediate: true,
   118  			handler() {
   119  				this.formFields = this.fields.map((field) => {
   120  					if (typeof field === "string")
   121  						return {
   122  							name: field,
   123  							label: field,
   124  							value: "",
   125  							type: "text",
   126  							dictionary: [],
   127  							separator: " ",
   128  							suggestion: undefined,
   129  						};
   130  
   131  					if (typeof field === "object")
   132  						return {
   133  							name: field.name || "",
   134  							label: field.label || "",
   135  							value: field.value || "",
   136  							type: field.type || "text",
   137  							dictionary:
   138  								field.dictionary instanceof Array ? field.dictionary : [],
   139  							separator: field.separator || " ",
   140  							suggestion: undefined,
   141  						};
   142  				});
   143  			},
   144  		},
   145  		"fields.length"() {
   146  			this.focus();
   147  		},
   148  		visible: {
   149  			immediate: true,
   150  			handler() {
   151  				this.focus();
   152  			},
   153  		},
   154  	},
   155  	methods: {
   156  		fieldType(f) {
   157  			var type = f.type || "text";
   158  			if (type !== "text" && type !== "password") return "text";
   159  			else return type;
   160  		},
   161  		handleMainClick() {
   162  			var data = {};
   163  			this.formFields.forEach((field) => {
   164  				var value = field.value;
   165  				if (field.type === "number") value = parseInt(value, 10) || 0;
   166  				else if (field.type === "float") value = parseFloat(value) || 0.0;
   167  				else if (field.type === "check") value = Boolean(value);
   168  				data[field.name] = value;
   169  			});
   170  
   171  			this.mainClick(data);
   172  		},
   173  		handleSecondClick() {
   174  			this.secondClick();
   175  		},
   176  		handleEscPressed() {
   177  			this.escPressed();
   178  		},
   179  		handleInput(index) {
   180  			// Create initial variable
   181  			var field = this.formFields[index],
   182  				dictionary = field.dictionary;
   183  
   184  			// Make sure dictionary is not empty
   185  			if (dictionary.length === 0) return;
   186  
   187  			// Fetch suggestion from dictionary
   188  			var words = field.value.split(field.separator),
   189  				lastWord = words[words.length - 1].toLowerCase(),
   190  				suggestion;
   191  
   192  			if (lastWord !== "") {
   193  				suggestion = dictionary.find((word) => {
   194  					return word.toLowerCase().startsWith(lastWord);
   195  				});
   196  			}
   197  
   198  			this.formFields[index].suggestion = suggestion;
   199  
   200  			// Make sure suggestion exist
   201  			if (suggestion == null) return;
   202  
   203  			// Display suggestion
   204  			this.$nextTick(() => {
   205  				var input = this.$refs.input[index],
   206  					suggestionNode = this.$refs["suggestion-" + index][0],
   207  					inputRect = input.getBoundingClientRect();
   208  
   209  				suggestionNode.style.top = inputRect.bottom - 1 + "px";
   210  				suggestionNode.style.left = inputRect.left + "px";
   211  			});
   212  		},
   213  		handleInputEnter(index) {
   214  			var suggestion = this.formFields[index].suggestion;
   215  
   216  			if (suggestion == null) {
   217  				this.handleMainClick();
   218  				return;
   219  			}
   220  
   221  			var separator = this.formFields[index].separator,
   222  				words = this.formFields[index].value.split(separator);
   223  
   224  			words.pop();
   225  			words.push(suggestion);
   226  
   227  			this.formFields[index].value = words.join(separator) + separator;
   228  			this.formFields[index].suggestion = undefined;
   229  			// Focus input again after suggestion is accepted
   230  			this.$refs.input[index].focus();
   231  		},
   232  		focus() {
   233  			this.$nextTick(() => {
   234  				if (!this.visible) return;
   235  
   236  				var fields = this.$refs.input,
   237  					otherInput = this.$el.querySelectorAll("input"),
   238  					button = this.$refs.mainButton;
   239  
   240  				if (fields && fields.length > 0) {
   241  					this.$refs.input[0].focus();
   242  					this.$refs.input[0].select();
   243  				} else if (otherInput && otherInput.length > 0) {
   244  					otherInput[0].focus();
   245  					otherInput[0].select();
   246  				} else if (button) {
   247  					button.focus();
   248  				}
   249  			});
   250  		},
   251  	},
   252  };