@skyservice-developers/vue-dev-kit 1.0.0

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,234 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <transition name="modal-fade">
4
+ <div v-if="modelValue" class="sky-modal-overlay" @click.self="handleOverlayClick">
5
+ <div class="sky-modal" :style="modalStyle">
6
+ <div class="sky-modal-header">
7
+ <button class="sky-modal-back" @click="close" :title="closeTitle">
8
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
9
+ <path d="M19 12H5M12 19l-7-7 7-7" stroke-linecap="round" stroke-linejoin="round"/>
10
+ </svg>
11
+ </button>
12
+ <div class="sky-modal-title-wrapper">
13
+ <h4 class="sky-modal-title">{{ title }}</h4>
14
+ <div v-if="subtitle" class="sky-modal-subtitle">{{ subtitle }}</div>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="sky-modal-body">
19
+ <slot></slot>
20
+ </div>
21
+
22
+ <div v-if="$slots.footer" class="sky-modal-footer">
23
+ <slot name="footer"></slot>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </transition>
28
+ </Teleport>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { computed, watch, onMounted, onUnmounted } from 'vue'
33
+
34
+ const props = defineProps({
35
+ modelValue: {
36
+ type: Boolean,
37
+ default: false
38
+ },
39
+ title: {
40
+ type: String,
41
+ default: ''
42
+ },
43
+ subtitle: {
44
+ type: String,
45
+ default: ''
46
+ },
47
+ closeTitle: {
48
+ type: String,
49
+ default: 'Закрити'
50
+ },
51
+ closeOnOverlay: {
52
+ type: Boolean,
53
+ default: true
54
+ },
55
+ closeOnEsc: {
56
+ type: Boolean,
57
+ default: true
58
+ },
59
+ width: {
60
+ type: String,
61
+ default: '100%'
62
+ },
63
+ height: {
64
+ type: String,
65
+ default: '100%'
66
+ }
67
+ })
68
+
69
+ const emit = defineEmits(['update:modelValue', 'close'])
70
+
71
+ const modalStyle = computed(() => ({
72
+ width: props.width,
73
+ height: props.height
74
+ }))
75
+
76
+ const close = () => {
77
+ emit('update:modelValue', false)
78
+ emit('close')
79
+ }
80
+
81
+ const handleOverlayClick = () => {
82
+ if (props.closeOnOverlay) {
83
+ close()
84
+ }
85
+ }
86
+
87
+ const handleKeydown = (e) => {
88
+ if (e.key === 'Escape' && props.closeOnEsc && props.modelValue) {
89
+ close()
90
+ }
91
+ }
92
+
93
+ watch(() => props.modelValue, (value) => {
94
+ if (value) {
95
+ document.body.style.overflow = 'hidden'
96
+ } else {
97
+ document.body.style.overflow = ''
98
+ }
99
+ })
100
+
101
+ onMounted(() => {
102
+ document.addEventListener('keydown', handleKeydown)
103
+ })
104
+
105
+ onUnmounted(() => {
106
+ document.removeEventListener('keydown', handleKeydown)
107
+ document.body.style.overflow = ''
108
+ })
109
+ </script>
110
+
111
+ <style scoped>
112
+ .sky-modal-overlay {
113
+ position: fixed;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ background: rgba(0, 0, 0, 0.6);
119
+ backdrop-filter: blur(2px);
120
+ z-index: var(--sky-modal-z-index, 9998);
121
+ display: flex;
122
+ justify-content: center;
123
+ align-items: center;
124
+ }
125
+
126
+ .sky-modal {
127
+ background: var(--sky-modal-bg, white);
128
+ border-radius: var(--sky-modal-radius, 0);
129
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.24);
130
+ display: flex;
131
+ flex-direction: column;
132
+ max-width: 100%;
133
+ max-height: 100%;
134
+ }
135
+
136
+ .sky-modal-header {
137
+ display: flex;
138
+ align-items: center;
139
+ padding: var(--sky-modal-header-padding, 10px 14px);
140
+ border-bottom: 1px solid var(--sky-modal-border-color, #dee2e6);
141
+ flex-shrink: 0;
142
+ }
143
+
144
+ .sky-modal-back {
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: 40px;
149
+ height: 40px;
150
+ padding: 0;
151
+ background: transparent;
152
+ border: none;
153
+ cursor: pointer;
154
+ border-radius: 6px;
155
+ transition: background-color 0.2s;
156
+ color: var(--sky-modal-back-color, #374151);
157
+ margin-right: 12px;
158
+ }
159
+
160
+ .sky-modal-back:hover {
161
+ background-color: var(--sky-modal-back-hover-bg, #f8f9fa);
162
+ }
163
+
164
+ .sky-modal-title-wrapper {
165
+ flex: 1;
166
+ min-width: 0;
167
+ }
168
+
169
+ .sky-modal-title {
170
+ margin: 0;
171
+ font-size: var(--sky-modal-title-size, 18px);
172
+ font-weight: var(--sky-modal-title-weight, 500);
173
+ color: var(--sky-modal-title-color, #252525);
174
+ line-height: 1.4;
175
+ white-space: nowrap;
176
+ overflow: hidden;
177
+ text-overflow: ellipsis;
178
+ }
179
+
180
+ .sky-modal-subtitle {
181
+ font-size: var(--sky-modal-subtitle-size, 14px);
182
+ color: var(--sky-modal-subtitle-color, #6c757d);
183
+ white-space: nowrap;
184
+ overflow: hidden;
185
+ text-overflow: ellipsis;
186
+ }
187
+
188
+ .sky-modal-body {
189
+ flex: 1;
190
+ overflow-y: auto;
191
+ overflow-x: hidden;
192
+ padding: var(--sky-modal-body-padding, 14px);
193
+ }
194
+
195
+ .sky-modal-footer {
196
+ padding: var(--sky-modal-footer-padding, 10px 14px);
197
+ border-top: 1px solid var(--sky-modal-border-color, #dee2e6);
198
+ display: flex;
199
+ justify-content: flex-end;
200
+ gap: 10px;
201
+ flex-shrink: 0;
202
+ }
203
+
204
+ /* Animations */
205
+ .modal-fade-enter-active,
206
+ .modal-fade-leave-active {
207
+ transition: opacity 0.3s ease;
208
+ }
209
+
210
+ .modal-fade-enter-active .sky-modal,
211
+ .modal-fade-leave-active .sky-modal {
212
+ transition: transform 0.3s ease;
213
+ }
214
+
215
+ .modal-fade-enter-from,
216
+ .modal-fade-leave-to {
217
+ opacity: 0;
218
+ }
219
+
220
+ .modal-fade-enter-from .sky-modal {
221
+ transform: translateY(-20px);
222
+ }
223
+
224
+ .modal-fade-leave-to .sky-modal {
225
+ transform: translateY(20px);
226
+ }
227
+
228
+ /* Responsive */
229
+ @media (min-width: 768px) {
230
+ .sky-modal {
231
+ border-radius: var(--sky-modal-radius, 8px);
232
+ }
233
+ }
234
+ </style>
@@ -0,0 +1,7 @@
1
+ import Header from './Header.vue'
2
+ import Dialog from './Dialog.vue'
3
+
4
+ export {
5
+ Header,
6
+ Dialog
7
+ }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Components
2
+ import { Header, Dialog } from './components'
3
+
4
+ export {
5
+ Header,
6
+ Dialog
7
+ }
@@ -0,0 +1,43 @@
1
+ import { DefineComponent } from 'vue'
2
+
3
+ // ============ Components ============
4
+
5
+ export interface HeaderProps {
6
+ title?: string
7
+ subtitle?: string
8
+ showBackButton?: boolean
9
+ backButtonTitle?: string
10
+ }
11
+
12
+ export interface HeaderSlots {
13
+ 'default'?: () => any
14
+ 'title'?: () => any
15
+ 'subtitle'?: () => any
16
+ }
17
+
18
+ export declare const Header: DefineComponent<HeaderProps>
19
+
20
+ // Dialog component
21
+ export interface DialogProps {
22
+ modelValue?: boolean
23
+ title?: string
24
+ subtitle?: string
25
+ zIndex?: number | string
26
+ closeText?: string
27
+ enableAnimation?: boolean
28
+ closeOnEsc?: boolean
29
+ mode?: 'next' | 'classic' | null
30
+ }
31
+
32
+ export interface DialogSlots {
33
+ 'default'?: () => any
34
+ 'buttons'?: () => any
35
+ }
36
+
37
+ export interface DialogEmits {
38
+ 'update:modelValue': (value: boolean) => void
39
+ 'close': () => void
40
+ 'save': () => void
41
+ }
42
+
43
+ export declare const Dialog: DefineComponent<DialogProps>
@@ -0,0 +1 @@
1
+ export { webviewCheck, isIosWebview, isAndroidWebview, isCefWebview, isWebview } from './webviewCheck'
@@ -0,0 +1,46 @@
1
+ import parser from 'ua-parser-js'
2
+
3
+ export function webviewCheck() {
4
+ // Check iOS webview
5
+ if (window.webkit != null) {
6
+ if (window.webkit.messageHandlers !== 'undefined') {
7
+ try {
8
+ var ua = parser(navigator.userAgent)
9
+ if (ua.browser.name === 'WebKit') {
10
+ return 'ios_webview'
11
+ }
12
+ } catch (err) {
13
+ console.error(err)
14
+ }
15
+ }
16
+ }
17
+
18
+ // Check Android webview
19
+ if (typeof Android !== 'undefined') {
20
+ return 'android_webview'
21
+ }
22
+
23
+ // Check CEF webview
24
+ if (typeof window.cefQuery !== 'undefined') {
25
+ return 'cef_webview'
26
+ }
27
+
28
+ return 'browser'
29
+ }
30
+
31
+ export function isIosWebview() {
32
+ return webviewCheck() === 'ios_webview'
33
+ }
34
+
35
+ export function isAndroidWebview() {
36
+ return webviewCheck() === 'android_webview'
37
+ }
38
+
39
+ export function isCefWebview() {
40
+ return webviewCheck() === 'cef_webview'
41
+ }
42
+
43
+ export function isWebview() {
44
+ const check = webviewCheck()
45
+ return check !== 'browser'
46
+ }