@skyservice-developers/vue-dev-kit 1.0.6 → 1.1.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,428 @@
1
+ <template>
2
+ <transition :name="enableAnimation ? 'dialog-slide' : ''">
3
+ <div
4
+ v-if="modelValue"
5
+ class="sky-dialogbox sky-dialogbox-next"
6
+ :style="[zIndex ? { 'z-index': zIndex } : null]"
7
+ >
8
+ <div class="sky-dialog-overlay" :class="{ 'sky-dialog-animate': enableAnimation }">
9
+ <div ref="dialogContent" class="sky-dialog-content">
10
+ <!-- Header -->
11
+ <button class="sky-dialog-back" :title="closeText" @click="close">
12
+ <svg width="15" height="15" viewBox="0 0 451.847 451.847" style="transform: rotate(90deg)">
13
+ <path fill="currentColor" d="M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z"/>
14
+ </svg>
15
+ </button>
16
+
17
+ <div class="sky-dialog-title" :class="{ 'sky-dialog-title-with-subtitle': subtitle }">
18
+ {{ title }}
19
+ <span v-if="subtitle" class="sky-dialog-subtitle">{{ subtitle }}</span>
20
+ </div>
21
+
22
+ <div class="sky-dialog-clearfix" />
23
+
24
+ <!-- Body -->
25
+ <div
26
+ ref="dialogPaper"
27
+ class="sky-dialog-paper"
28
+ :class="{ 'sky-dialog-paper-no-footer': !showFooter }"
29
+ @touchstart="handleTouchStart"
30
+ @touchend="handleTouchEnd"
31
+ >
32
+ <!-- iOS swipe back area -->
33
+ <div v-if="isIos" class="sky-dialog-swipe-area" />
34
+ <slot></slot>
35
+ </div>
36
+
37
+ <!-- Footer -->
38
+ <div v-if="showFooter" class="sky-dialog-footer" :class="{ 'sky-dialog-footer-animate': enableAnimation }">
39
+ <slot name="buttons"></slot>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </transition>
45
+ </template>
46
+
47
+ <script>
48
+ import { isIosWebview, isAndroidWebview } from '../../shared/utils/webviewCheck'
49
+
50
+ export default {
51
+ name: 'DialogNext',
52
+ props: {
53
+ modelValue: {
54
+ type: Boolean,
55
+ default: false
56
+ },
57
+ title: {
58
+ type: String,
59
+ default: ''
60
+ },
61
+ subtitle: {
62
+ type: String,
63
+ default: ''
64
+ },
65
+ zIndex: {
66
+ type: [Number, String],
67
+ default: null
68
+ },
69
+ closeText: {
70
+ type: String,
71
+ default: 'Назад'
72
+ },
73
+ enableAnimation: {
74
+ type: Boolean,
75
+ default: true
76
+ },
77
+ closeOnEsc: {
78
+ type: Boolean,
79
+ default: true
80
+ },
81
+ hasButtons: {
82
+ type: Boolean,
83
+ default: null
84
+ }
85
+ },
86
+ data() {
87
+ return {
88
+ touchStartX: 0
89
+ }
90
+ },
91
+ computed: {
92
+ isIos() {
93
+ try {
94
+ return isIosWebview()
95
+ } catch {
96
+ return false
97
+ }
98
+ },
99
+ isAndroid() {
100
+ try {
101
+ return isAndroidWebview()
102
+ } catch {
103
+ return false
104
+ }
105
+ },
106
+ // Determine if footer should be shown
107
+ showFooter() {
108
+ // If hasButtons prop is explicitly set, use it
109
+ if (this.hasButtons !== null) {
110
+ return this.hasButtons
111
+ }
112
+ // Fallback to slot check (for direct usage without Dialog wrapper)
113
+ return !!this.$slots.buttons
114
+ }
115
+ },
116
+ watch: {
117
+ // Body scroll lock
118
+ modelValue(value) {
119
+ if (value) {
120
+ document.body.style.overflow = 'hidden'
121
+ this.$nextTick(() => {
122
+ this.androidFix()
123
+ })
124
+ } else {
125
+ document.body.style.overflow = ''
126
+ }
127
+ }
128
+ },
129
+ mounted() {
130
+ document.addEventListener('keydown', this.handleKeydown)
131
+ window.addEventListener('resize', this.androidFix)
132
+ },
133
+ beforeDestroy() {
134
+ document.removeEventListener('keydown', this.handleKeydown)
135
+ window.removeEventListener('resize', this.androidFix)
136
+ document.body.style.overflow = ''
137
+ },
138
+ methods: {
139
+ close() {
140
+ this.$emit('update:modelValue', false)
141
+ this.$emit('close')
142
+ },
143
+ handleKeydown(e) {
144
+ if (e.key === 'Escape' && this.closeOnEsc && this.modelValue) {
145
+ this.close()
146
+ }
147
+ if (e.key === 'Enter' && this.modelValue) {
148
+ this.$emit('save')
149
+ }
150
+ },
151
+ // Touch handling for iOS swipe back
152
+ handleTouchStart(e) {
153
+ if (e.touches[0].clientX < 35) {
154
+ this.touchStartX = e.touches[0].clientX
155
+ }
156
+ },
157
+ handleTouchEnd(e) {
158
+ if (this.touchStartX > 0 && this.touchStartX < 35) {
159
+ const touchEndX = e.changedTouches[0].clientX
160
+ if (touchEndX - this.touchStartX > 50) {
161
+ this.close()
162
+ }
163
+ }
164
+ this.touchStartX = 0
165
+ },
166
+ // Android notch fix
167
+ androidFix() {
168
+ if (!this.isAndroid || !this.$refs.dialogContent) return
169
+
170
+ try {
171
+ if (typeof Android !== 'undefined' && Android.getDisplayCutoutTop) {
172
+ const cutoutTop = Android.getDisplayCutoutTop()
173
+ if (cutoutTop && window.devicePixelRatio > 1.0) {
174
+ const paddingTop = cutoutTop / window.devicePixelRatio
175
+ this.$refs.dialogContent.style.paddingTop = paddingTop + 'px'
176
+ }
177
+ }
178
+ } catch (err) {
179
+ // Android interface not available
180
+ }
181
+ }
182
+ }
183
+ }
184
+ </script>
185
+
186
+ <style>
187
+ /* Global styles (не scoped через баг з Teleport) */
188
+ .sky-dialogbox-next {
189
+ display: block;
190
+ position: fixed;
191
+ padding: 0;
192
+ top: 0;
193
+ left: 0;
194
+ width: 100%;
195
+ height: 100%;
196
+ z-index: var(--sky-dialog-z-index, 9998);
197
+ background: rgba(0, 0, 0, 0.6);
198
+ backdrop-filter: blur(2px);
199
+ }
200
+ </style>
201
+
202
+ <style scoped>
203
+ .sky-dialog-overlay {
204
+ display: flex;
205
+ justify-content: center;
206
+ align-items: center;
207
+ position: fixed;
208
+ padding: 0;
209
+ top: 0;
210
+ left: 0;
211
+ width: 100%;
212
+ height: 100%;
213
+ z-index: 9999;
214
+ }
215
+
216
+ .sky-dialog-content {
217
+ background: var(--sky-dialog-bg, white);
218
+ width: 100%;
219
+ height: 100%;
220
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.24);
221
+ }
222
+
223
+ .sky-dialog-back {
224
+ cursor: pointer;
225
+ margin: 10px;
226
+ padding: 14px 12px 6px;
227
+ float: left;
228
+ line-height: 1;
229
+ background: transparent;
230
+ border: none;
231
+ border-radius: 6px;
232
+ color: var(--sky-dialog-back-color, #374151);
233
+ transition: background-color 0.2s;
234
+ }
235
+
236
+ .sky-dialog-back:hover {
237
+ background-color: var(--sky-dialog-back-hover-bg, #f8f9fa);
238
+ }
239
+
240
+ .sky-dialog-title {
241
+ max-width: calc(100% - 80px);
242
+ font-size: var(--sky-dialog-title-size, 13pt);
243
+ padding: 21px 0;
244
+ padding-right: 0;
245
+ float: left;
246
+ white-space: nowrap;
247
+ overflow: hidden;
248
+ text-overflow: ellipsis;
249
+ color: var(--sky-dialog-title-color, #252525);
250
+ }
251
+
252
+ .sky-dialog-title-with-subtitle {
253
+ padding: 13px 0;
254
+ }
255
+
256
+ .sky-dialog-subtitle {
257
+ display: block;
258
+ font-size: var(--sky-dialog-subtitle-size, 12pt);
259
+ line-height: 24px;
260
+ color: var(--sky-dialog-subtitle-color, #6c757d);
261
+ white-space: nowrap;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ }
265
+
266
+ .sky-dialog-clearfix {
267
+ clear: both;
268
+ }
269
+
270
+ .sky-dialog-paper {
271
+ height: 100%;
272
+ overflow-y: auto;
273
+ overflow-x: hidden;
274
+ -webkit-overflow-scrolling: touch;
275
+ position: relative;
276
+ }
277
+
278
+ .sky-dialog-swipe-area {
279
+ position: absolute;
280
+ width: 35px;
281
+ height: 100%;
282
+ left: 0;
283
+ }
284
+
285
+ .sky-dialog-footer {
286
+ padding: 5px 10px;
287
+ display: flex;
288
+ justify-content: center;
289
+ width: 100%;
290
+ transform: translateY(-52px);
291
+ gap: 10px;
292
+ }
293
+
294
+ /* Кнопки в футері: 1 = 100%, 2 = по 50% */
295
+ .sky-dialog-footer > :deep(*) {
296
+ flex: 1;
297
+ min-width: 0;
298
+ }
299
+
300
+ .sky-dialog-footer:has(> :deep(*:only-child)) > :deep(*) {
301
+ max-width: 100%;
302
+ }
303
+
304
+ .sky-dialog-footer:has(> :deep(*:nth-child(2)):not(:has(> :deep(*:nth-child(3))))) > :deep(*) {
305
+ flex: 1 1 50%;
306
+ }
307
+
308
+ /* Desktop */
309
+ @media only screen and (min-width: 1400px) {
310
+ .sky-dialog-content {
311
+ width: 100%;
312
+ margin: 0 auto;
313
+ }
314
+ }
315
+
316
+ /* Tablet and Desktop */
317
+ @media screen and (min-width: 710px) {
318
+ .sky-dialog-paper {
319
+ height: calc(100% - 150px);
320
+ max-height: calc(100% - 150px);
321
+ background-color: #fff;
322
+ margin: 0 10px 60px 10px;
323
+ }
324
+
325
+ /* Full height when no footer */
326
+ .sky-dialog-paper-no-footer {
327
+ height: calc(100% - 70px);
328
+ max-height: calc(100% - 70px);
329
+ margin-bottom: 10px;
330
+ }
331
+ }
332
+
333
+ /* Mobile */
334
+ @media screen and (max-width: 709px) {
335
+ .sky-dialog-paper {
336
+ height: calc(100% - 142px);
337
+ max-height: calc(100% - 142px);
338
+ background-color: #fff;
339
+ margin: 0 10px 10px 10px;
340
+ max-width: 100vw !important;
341
+ }
342
+
343
+ /* Full height when no footer */
344
+ .sky-dialog-paper-no-footer {
345
+ height: calc(100% - 60px);
346
+ max-height: calc(100% - 60px);
347
+ }
348
+
349
+ .sky-dialog-footer {
350
+ transform: translateY(-6px);
351
+ }
352
+ }
353
+
354
+ @media screen and (max-width: 500px) {
355
+ .sky-dialog-subtitle {
356
+ display: block;
357
+ white-space: nowrap;
358
+ overflow: hidden;
359
+ text-overflow: ellipsis;
360
+ }
361
+
362
+ .sky-dialog-title-with-subtitle {
363
+ padding: 12px 24px !important;
364
+ }
365
+ }
366
+
367
+ @media screen and (max-width: 374px) {
368
+ .sky-dialog-subtitle {
369
+ font-size: 9pt;
370
+ }
371
+ }
372
+
373
+ /* iPhone safe area support */
374
+ @supports (padding-top: env(safe-area-inset-top)) {
375
+ .sky-dialog-paper {
376
+ height: calc(100% - 150px - env(safe-area-inset-top));
377
+ }
378
+
379
+ /* Full height when no footer */
380
+ .sky-dialog-paper-no-footer {
381
+ height: calc(100% - 60px - env(safe-area-inset-top));
382
+ }
383
+
384
+ .sky-dialog-footer {
385
+ padding-bottom: calc(env(safe-area-inset-bottom) + 8px);
386
+ }
387
+ }
388
+
389
+ /* Animations */
390
+ .sky-dialog-animate {
391
+ animation: sky-dialog-slide-in 0.4s ease-in-out;
392
+ }
393
+
394
+ .sky-dialog-footer-animate {
395
+ animation: sky-dialog-footer-in 0.4s ease-in-out;
396
+ }
397
+
398
+ @keyframes sky-dialog-slide-in {
399
+ 0% {
400
+ opacity: 0;
401
+ margin-top: -1600px;
402
+ }
403
+ 100% {
404
+ opacity: 1;
405
+ margin-top: 0;
406
+ }
407
+ }
408
+
409
+ @keyframes sky-dialog-footer-in {
410
+ 0% {
411
+ opacity: 0;
412
+ bottom: -100px;
413
+ }
414
+ 50% {
415
+ opacity: 0.25;
416
+ bottom: -50px;
417
+ }
418
+ 100% {
419
+ opacity: 1;
420
+ bottom: 15px;
421
+ }
422
+ }
423
+
424
+ /* Transition */
425
+ .dialog-slide-leave-active {
426
+ animation: sky-dialog-slide-in 0.4s reverse;
427
+ }
428
+ </style>
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <header class="sky-header">
3
+ <div class="header-content">
4
+ <div class="header-top">
5
+ <div class="header-title-wrapper">
6
+ <button
7
+ v-if="shouldShowBackButton"
8
+ class="btn-back"
9
+ @click="handleBack"
10
+ :title="backButtonTitle"
11
+ >
12
+ <svg width="15" height="15" viewBox="0 0 451.847 451.847" style="transform: rotate(90deg)">
13
+ <path fill="currentColor" d="M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z"/>
14
+ </svg>
15
+ </button>
16
+ <div class="header-title-content">
17
+ <slot name="title">
18
+ <h4 class="header-title">{{ title }} vue 2</h4>
19
+ </slot>
20
+ <slot name="subtitle">
21
+ <div v-if="subtitle" class="header-subtitle">{{ subtitle }}</div>
22
+ </slot>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="header-actions">
27
+ <slot></slot>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </header>
32
+ </template>
33
+
34
+ <script>
35
+ export default {
36
+ name: 'Header',
37
+ props: {
38
+ title: {
39
+ type: String,
40
+ default: ''
41
+ },
42
+ subtitle: {
43
+ type: String,
44
+ default: ''
45
+ },
46
+ showBackButton: {
47
+ type: Boolean,
48
+ default: true
49
+ },
50
+ backButtonTitle: {
51
+ type: String,
52
+ default: 'Назад'
53
+ },
54
+ backEvent: {
55
+ type: Function,
56
+ default: null
57
+ }
58
+ },
59
+ computed: {
60
+ // Перевіряємо чи сторінка в iframe
61
+ isInIframe() {
62
+ try {
63
+ return window.self !== window.top
64
+ } catch (e) {
65
+ return true
66
+ }
67
+ },
68
+ // Показуємо кнопку якщо є backEvent АБО (showBackButton=true І сторінка в iframe)
69
+ shouldShowBackButton() {
70
+ return this.backEvent || (this.showBackButton && this.isInIframe)
71
+ }
72
+ },
73
+ methods: {
74
+ // Обробник кнопки "Назад" - викликає backEvent або відправляє повідомлення батьківському вікну
75
+ handleBack() {
76
+ if (this.backEvent) {
77
+ this.backEvent()
78
+ } else {
79
+ window.parent.postMessage({ type: 'exit' }, '*')
80
+ }
81
+ }
82
+ }
83
+ }
84
+ </script>
85
+
86
+ <style scoped>
87
+ .sky-header {
88
+ position: sticky;
89
+ top: 0;
90
+ left: 0;
91
+ right: 0;
92
+ background: var(--sky-header-bg, white);
93
+ border-bottom: 1px solid var(--sky-header-border-color, #dee2e6);
94
+ z-index: var(--sky-header-z-index, 100);
95
+ padding: var(--sky-header-padding, 10px 0);
96
+ }
97
+
98
+ .header-content {
99
+ padding: var(--sky-header-content-padding, 4px 14px);
100
+ margin: 0 auto;
101
+ }
102
+
103
+ .header-top {
104
+ display: flex;
105
+ justify-content: space-between;
106
+ align-items: center;
107
+ }
108
+
109
+ .header-title-wrapper {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 12px;
113
+ }
114
+
115
+ .header-title-content {
116
+ display: flex;
117
+ flex-direction: column;
118
+ }
119
+
120
+ .btn-back {
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ width: 32px;
125
+ height: 32px;
126
+ padding: 0;
127
+ background: transparent;
128
+ border: none;
129
+ cursor: pointer;
130
+ border-radius: 6px;
131
+ transition: background-color 0.2s;
132
+ color: var(--sky-header-back-btn-color, #374151);
133
+ }
134
+
135
+ .btn-back img,
136
+ .btn-back svg {
137
+ display: block;
138
+ }
139
+
140
+ .btn-back:hover {
141
+ background-color: var(--sky-header-back-btn-hover-bg, #f8f9fa);
142
+ }
143
+
144
+ .btn-back:active {
145
+ background-color: var(--sky-header-back-btn-active-bg, #e9ecef);
146
+ }
147
+
148
+ .header-title {
149
+ margin: 0;
150
+ font-size: var(--sky-header-title-size, 18px);
151
+ font-weight: var(--sky-header-title-weight, 500);
152
+ color: var(--sky-header-title-color, #252525);
153
+ line-height: 1.5;
154
+ user-select: none;
155
+ }
156
+
157
+ .header-subtitle {
158
+ font-size: var(--sky-header-subtitle-size, 14px);
159
+ color: var(--sky-header-subtitle-color, #6c757d);
160
+ font-weight: 400;
161
+ line-height: 1.5;
162
+ }
163
+
164
+ .header-actions {
165
+ display: flex;
166
+ gap: var(--sky-header-actions-gap, 12px);
167
+ }
168
+
169
+ @media (max-width: 768px) {
170
+ /* .header-content {
171
+ padding: 12px 16px;
172
+ } */
173
+
174
+ .header-top {
175
+ flex-direction: column;
176
+ align-items: flex-start;
177
+ /* gap: 12px; */
178
+ }
179
+
180
+ .header-actions {
181
+ width: 100%;
182
+ justify-content: flex-end;
183
+ }
184
+ }
185
+ </style>