@fy-/fws-vue 2.3.80 → 2.3.82

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.
@@ -0,0 +1,174 @@
1
+ <script setup lang="ts">
2
+ import { useDebounceFn } from '@vueuse/core'
3
+ import { nextTick, onMounted, onUnmounted, ref } from 'vue'
4
+ import { useEventBus } from '../../composables/event-bus'
5
+ import DefaultInput from './DefaultInput.vue'
6
+ import DefaultModal from './DefaultModal.vue'
7
+
8
+ const eventBus = useEventBus()
9
+ const title = ref<string | null>(null)
10
+ const desc = ref<string | null>(null)
11
+ const inputLabel = ref<string | undefined>(undefined)
12
+ const inputPlaceholder = ref<string | undefined>(undefined)
13
+ const expectedValue = ref<string | null>(null)
14
+ const errorMessage = ref<string | null>(null)
15
+ const onConfirm = ref<Function | null>(null)
16
+ const isOpen = ref<boolean>(false)
17
+ const modalRef = ref<HTMLElement | null>(null)
18
+ const inputValue = ref<string>('')
19
+ const inputError = ref<string>('')
20
+ let previouslyFocusedElement: HTMLElement | null = null
21
+
22
+ interface ConfirmWithInputModalData {
23
+ title: string
24
+ desc: string
25
+ inputLabel?: string
26
+ inputPlaceholder?: string
27
+ expectedValue?: string
28
+ errorMessage?: string
29
+ onConfirm: (inputValue: string) => void | Promise<void>
30
+ }
31
+
32
+ const _onConfirm = useDebounceFn(async () => {
33
+ // If expectedValue is set, validate the input
34
+ if (expectedValue.value && inputValue.value.toLocaleLowerCase() !== expectedValue.value.toLocaleLowerCase()) {
35
+ inputError.value = errorMessage.value || 'Incorrect value entered'
36
+ return
37
+ }
38
+
39
+ if (onConfirm.value) {
40
+ await onConfirm.value(inputValue.value)
41
+ }
42
+ resetConfirm()
43
+ }, 300)
44
+
45
+ function resetConfirm() {
46
+ title.value = null
47
+ desc.value = null
48
+ inputLabel.value = undefined
49
+ inputPlaceholder.value = undefined
50
+ expectedValue.value = null
51
+ errorMessage.value = null
52
+ onConfirm.value = null
53
+ isOpen.value = false
54
+ inputValue.value = ''
55
+ inputError.value = ''
56
+ eventBus.emit('confirmWithInputModal', false)
57
+ if (previouslyFocusedElement) {
58
+ previouslyFocusedElement.focus()
59
+ }
60
+ }
61
+
62
+ function showConfirmWithInput(data: ConfirmWithInputModalData) {
63
+ title.value = data.title
64
+ desc.value = data.desc
65
+ inputLabel.value = data.inputLabel || undefined
66
+ inputPlaceholder.value = data.inputPlaceholder || undefined
67
+ expectedValue.value = data.expectedValue || null
68
+ errorMessage.value = data.errorMessage || null
69
+ onConfirm.value = data.onConfirm
70
+ inputValue.value = ''
71
+ inputError.value = ''
72
+
73
+ // Emit event first to ensure it's registered before opening the modal
74
+ eventBus.emit('confirmWithInputModal', true)
75
+
76
+ // Use requestAnimationFrame instead of setTimeout for better performance
77
+ requestAnimationFrame(() => {
78
+ isOpen.value = true
79
+ eventBus.emit('confirmWithInputModal', true)
80
+
81
+ nextTick(() => {
82
+ previouslyFocusedElement = document.activeElement as HTMLElement
83
+ try {
84
+ modalRef.value?.focus()
85
+ }
86
+ catch {
87
+ }
88
+ })
89
+ })
90
+ }
91
+
92
+ onMounted(() => {
93
+ eventBus.on('resetConfirmWithInput', resetConfirm)
94
+ eventBus.on('showConfirmWithInput', showConfirmWithInput)
95
+ })
96
+
97
+ onUnmounted(() => {
98
+ eventBus.off('resetConfirmWithInput', resetConfirm)
99
+ eventBus.off('showConfirmWithInput', showConfirmWithInput)
100
+ })
101
+ </script>
102
+
103
+ <template>
104
+ <DefaultModal
105
+ id="confirmWithInput"
106
+ ref="modalRef"
107
+ m-size="!max-w-3xl w-full"
108
+ >
109
+ <div
110
+ class="bg-gradient-to-br from-gray-900/70 to-gray-800/50 rounded-lg border border-gray-700/30 overflow-hidden"
111
+ :aria-labelledby="title ? 'confirm-input-modal-title' : undefined"
112
+ :aria-describedby="desc ? 'confirm-input-modal-desc' : undefined"
113
+ aria-modal="true"
114
+ role="dialog"
115
+ tabindex="-1"
116
+ >
117
+ <!-- Header with gradient background -->
118
+ <div
119
+ v-if="title"
120
+ class="bg-gradient-to-r from-indigo-900/30 to-indigo-800/20 p-4 border-b border-indigo-700/30"
121
+ >
122
+ <h3
123
+ id="confirm-input-modal-title"
124
+ class="text-xl font-semibold text-white"
125
+ >
126
+ {{ title }}
127
+ </h3>
128
+ </div>
129
+
130
+ <!-- Content area with styled box -->
131
+ <div class="p-5 text-fv-neutral-100">
132
+ <div v-if="desc" class="bg-gradient-to-r from-blue-950/50 to-indigo-950/50 p-4 rounded-lg border border-blue-700/30 mb-6 shadow-md">
133
+ <p
134
+ id="confirm-input-modal-desc"
135
+ class="text-sm sm:text-base text-gray-200 prose prose-invert prose-sm min-w-full"
136
+ v-html="desc"
137
+ />
138
+ </div>
139
+
140
+ <!-- Input field -->
141
+ <div class="mb-6">
142
+ <DefaultInput
143
+ id="confirmInput"
144
+ v-model="inputValue"
145
+ type="text"
146
+ :label="inputLabel"
147
+ :placeholder="inputPlaceholder"
148
+ :error="inputError"
149
+ autocomplete="off"
150
+ class="w-full"
151
+ @keyup.enter="_onConfirm()"
152
+ />
153
+ </div>
154
+
155
+ <!-- Action buttons with modern styling -->
156
+ <div class="flex justify-center gap-4 mt-6">
157
+ <button
158
+ class="btn accent large shadow-lg hover:shadow-fuchsia-500/30 transition-all duration-300 hover:scale-105 active:scale-95 px-8 rounded-lg"
159
+ :disabled="!inputValue.trim()"
160
+ @click="_onConfirm()"
161
+ >
162
+ {{ $t("confirm_modal_cta_confirm") }}
163
+ </button>
164
+ <button
165
+ class="btn neutral large shadow-md transform transition hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-gray-500 px-8 rounded-lg"
166
+ @click="resetConfirm()"
167
+ >
168
+ {{ $t("confirm_modal_cta_cancel") }}
169
+ </button>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </DefaultModal>
174
+ </template>
package/index.ts CHANGED
@@ -17,6 +17,7 @@ import UserProfileStrict from './components/fws/UserProfileStrict.vue'
17
17
  import { ClientOnly } from './components/ssr/ClientOnly'
18
18
  import DefaultBreadcrumb from './components/ui/DefaultBreadcrumb.vue'
19
19
  import DefaultConfirm from './components/ui/DefaultConfirm.vue'
20
+ import DefaultConfirmWithInput from './components/ui/DefaultConfirmWithInput.vue'
20
21
  import DefaultDropdown from './components/ui/DefaultDropdown.vue'
21
22
 
22
23
  import DefaultDropdownLink from './components/ui/DefaultDropdownLink.vue'
@@ -103,6 +104,7 @@ export {
103
104
  DataTable,
104
105
  DefaultBreadcrumb,
105
106
  DefaultConfirm,
107
+ DefaultConfirmWithInput,
106
108
  DefaultDropdown,
107
109
  DefaultDropdownLink,
108
110
  DefaultGallery,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.3.80",
3
+ "version": "2.3.82",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",