adata-ui 2.1.40-beta.10 → 2.1.40-beta.12
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.
- package/.claude/settings.local.json +19 -0
- package/assets/styles/index.scss +67 -5
- package/components/elements/a-select-row/ASelectRowV2.vue +8 -8
- package/components/elements/a-select-row/index.vue +3 -5
- package/components/elements/accordion/AAccordion.vue +5 -3
- package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +3 -3
- package/components/elements/alert/AAlert.vue +34 -47
- package/components/elements/bg-block/ABgBlock.vue +1 -1
- package/components/elements/button/AButton.vue +3 -3
- package/components/elements/button/AButtonV2.vue +7 -5
- package/components/elements/button-login/index.vue +1 -1
- package/components/elements/chip-wrapper/AChipWrapper.vue +1 -1
- package/components/elements/chips/AChips.vue +2 -2
- package/components/elements/digit-badge/ADigitBadge.vue +21 -19
- package/components/elements/feature-description/AFeatureDescription.vue +1 -1
- package/components/elements/leave-note/ALeaveNote.vue +1 -1
- package/components/elements/pagination/APagination.vue +7 -7
- package/components/elements/segmented/ASegmentedV2.vue +103 -10
- package/components/elements/select/ASelect.vue +4 -4
- package/components/elements/select/ASelectMobile.vue +2 -2
- package/components/elements/select/ASelectV2.vue +10 -11
- package/components/elements/select/ui/select-button.vue +1 -1
- package/components/elements/status-badge/AStatusBadge.vue +22 -19
- package/components/elements/status-badge-v2/StatusBadgeV2.vue +1 -3
- package/components/elements/table/ATable.vue +1 -1
- package/components/elements/table/table.config.ts +2 -2
- package/components/elements/tree-select/ATreeSelect.vue +97 -113
- package/components/elements/tree-select/TreeService.ts +142 -207
- package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -0
- package/components/elements/tree-select/types.ts +40 -11
- package/components/elements/tree-select/utils.ts +30 -0
- package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -0
- package/components/features/color-mode/AColorMode.client.vue +1 -1
- package/components/features/dropdown/ADropdownV2.vue +1 -2
- package/components/features/lang-switcher/lang-switcher.vue +9 -4
- package/components/features/payment/banner/BasicPlusLimitBanner.vue +3 -3
- package/components/features/payment/banner/PaymentBanner.vue +3 -3
- package/components/features/payment/banner/UpSellBanner.vue +3 -3
- package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +43 -27
- package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +34 -23
- package/components/features/payment/process/PaymentMethodSidePanel.vue +61 -49
- package/components/features/payment/process/PaymentProcess.vue +6 -6
- package/components/features/payment/process/PaymentTopUpSidePanel.vue +44 -63
- package/components/features/pk-mobile-services/APkMobileServices.vue +1 -1
- package/components/features//321/201hange-version/AChangeVersion.vue +2 -2
- package/components/forms/checkbox/ACheckbox.vue +1 -1
- package/components/forms/checkbox/ACheckboxV2.vue +5 -5
- package/components/forms/input/AInputV2.vue +4 -4
- package/components/forms/input/date/AInputDate.vue +2 -2
- package/components/forms/input/password/AInputPassword.vue +8 -2
- package/components/forms/input/standard/AInputStandard.vue +3 -3
- package/components/forms/input/textarea/ATextarea.vue +2 -2
- package/components/forms/toggle/AToggle.vue +2 -3
- package/components/forms/toggle/AToggleV2.vue +3 -3
- package/components/modals/AnotherDeviceModal.vue +1 -1
- package/components/modals/ContactsMobileModel.vue +1 -1
- package/components/modals/ContentNavigationModal.vue +77 -333
- package/components/modals/Insufficient-funds-modal.vue +1 -1
- package/components/modals/PaymentMethodModal.vue +1 -1
- package/components/modals/id/IdBanner.vue +3 -3
- package/components/modals/id/IdRecoveryModal.vue +1 -1
- package/components/navigation/bottom-navigation/ABottomNavigation.vue +1 -1
- package/components/navigation/breadcrumbs/ABreadcrumbs.vue +106 -45
- package/components/navigation/breadcrumbs/types.ts +3 -0
- package/components/navigation/footer/AFooter.vue +39 -40
- package/components/navigation/footer/ui/a-footer-accordion.vue +9 -9
- package/components/navigation/header/AHeader.vue +13 -19
- package/components/navigation/header/AlmatyContacts.vue +78 -78
- package/components/navigation/header/CardGallery.vue +4 -4
- package/components/navigation/header/HeaderUsage.vue +4 -6
- package/components/navigation/header/ListItem.vue +2 -2
- package/components/navigation/header/NavCard.vue +1 -1
- package/components/navigation/header/NavList.vue +2 -2
- package/components/navigation/header/NotificationsMenu.vue +2 -3
- package/components/navigation/header/ProductMenu.vue +7 -7
- package/components/navigation/header/ProfileMenu.vue +26 -35
- package/components/navigation/header/SystemNotification.vue +1 -3
- package/components/navigation/pill-tabs/APillTabs.vue +2 -2
- package/components/navigation/pill-tabs/APillTabsV2.vue +22 -6
- package/components/overlays/drawer/ADrawerV2.vue +335 -0
- package/components/overlays/modal/AModal.vue +4 -4
- package/components/overlays/modal/AModalV2.vue +7 -4
- package/components/overlays/side-panel/ASidePanel.vue +6 -6
- package/components/overlays/sideover/ASlideover.vue +6 -6
- package/components/overlays/tooltip/ATooltip.vue +1 -1
- package/components/overlays/tooltip/ATooltipV2.vue +2 -12
- package/components/transitions/ATransitionHeight.vue +1 -1
- package/composables/useHeaderNavigationLinks.ts +100 -94
- package/icons/archive.vue +16 -0
- package/icons/arrow/arrows-in.vue +11 -0
- package/icons/arrow/arrows-out.vue +11 -0
- package/icons/arrow/arrows-resize-in.vue +38 -0
- package/icons/arrow/arrows-resize-out.vue +17 -0
- package/icons/bot.vue +52 -0
- package/icons/clock-time.vue +24 -0
- package/icons/compare-outlined.vue +8 -0
- package/icons/dots-horizontal.vue +31 -0
- package/icons/download-line.vue +13 -0
- package/icons/egov.vue +41 -0
- package/icons/elicense.vue +38 -0
- package/icons/favourite-bookmark-outline.vue +17 -0
- package/icons/file-pen.vue +31 -0
- package/icons/file-stack.vue +31 -0
- package/icons/file-types/excel.vue +131 -0
- package/icons/file-types/html.vue +13 -0
- package/icons/file-types/jpeg.vue +16 -0
- package/icons/file-types/pdf.vue +11 -0
- package/icons/file-types/png.vue +11 -0
- package/icons/file-types/rar.vue +11 -0
- package/icons/file-types/word.vue +50 -0
- package/icons/file-types/zip.vue +11 -0
- package/icons/git-compare.vue +38 -0
- package/icons/graduation-cap.vue +31 -0
- package/icons/graph-loading.vue +34 -0
- package/icons/help-question-circle.vue +26 -0
- package/icons/in-row-type.vue +46 -0
- package/icons/in-table-type.vue +29 -0
- package/icons/message-dotted.vue +30 -0
- package/icons/notification-error.vue +17 -0
- package/icons/person.vue +22 -0
- package/icons/settings.vue +26 -0
- package/icons/shield-alert.vue +31 -0
- package/icons/socials/facebook-color.vue +16 -0
- package/icons/socials/globe-color.vue +16 -0
- package/icons/socials/instagram-color.vue +46 -0
- package/icons/socials/linkedin-color.vue +14 -0
- package/icons/socials/telegram-color.vue +16 -0
- package/icons/socials/tiktok-color.vue +14 -0
- package/icons/socials/vk-color.vue +16 -0
- package/icons/socials/youtube-color.vue +16 -0
- package/icons/view-eye-open.vue +13 -0
- package/lang/en.ts +1 -0
- package/lang/kk.ts +1 -0
- package/lang/ru.ts +1 -0
- package/package.json +1 -1
- package/plugins/toast.client.ts +38 -55
- package/tailwind.config.ts +147 -116
- package/utils/twMerge.ts +34 -0
- package/components/elements/tree-select/components/tree-select-nodes.vue +0 -91
|
@@ -18,18 +18,22 @@ const props = withDefaults(defineProps<{
|
|
|
18
18
|
block?: boolean
|
|
19
19
|
disabled?: boolean
|
|
20
20
|
size?: 'sm' | 'md'
|
|
21
|
+
scrollIntoView?: boolean
|
|
21
22
|
}>(), {
|
|
22
23
|
showCount: false,
|
|
23
24
|
block: false,
|
|
24
25
|
disabled: false,
|
|
25
26
|
size: 'sm',
|
|
27
|
+
scrollIntoView: false,
|
|
26
28
|
})
|
|
27
29
|
|
|
28
30
|
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
|
|
29
31
|
|
|
32
|
+
const listRef = ref<HTMLElement | null>(null)
|
|
33
|
+
|
|
30
34
|
const SIZE_CLASS: Record<'sm' | 'md', string> = {
|
|
31
|
-
sm: 'text-
|
|
32
|
-
md: 'text-
|
|
35
|
+
sm: 'text-body-sm',
|
|
36
|
+
md: 'text-body-md',
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
const COUNT_COLOR_CLASS: Record<CountColor, string> = {
|
|
@@ -55,11 +59,22 @@ function select(option: PillTab) {
|
|
|
55
59
|
if (isDisabled(option)) return
|
|
56
60
|
emit('update:modelValue', option.key)
|
|
57
61
|
}
|
|
62
|
+
|
|
63
|
+
function scrollActiveIntoView() {
|
|
64
|
+
if (!props.scrollIntoView) return
|
|
65
|
+
nextTick(() => {
|
|
66
|
+
const active = listRef.value?.querySelector<HTMLElement>('[aria-selected="true"]')
|
|
67
|
+
active?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
watch(() => props.modelValue, scrollActiveIntoView)
|
|
72
|
+
onMounted(scrollActiveIntoView)
|
|
58
73
|
</script>
|
|
59
74
|
|
|
60
75
|
<template>
|
|
61
76
|
<div
|
|
62
|
-
class="pill-tabs-v2 min-w-0 max-w-full rounded-
|
|
77
|
+
class="pill-tabs-v2 min-w-0 max-w-full rounded-lg border border-gray-200 bg-gray-50 p-1 dark:border-white/10 dark:bg-white/[0.03]"
|
|
63
78
|
:class="[
|
|
64
79
|
block ? 'w-full' : 'w-fit',
|
|
65
80
|
disabled ? 'pill-tabs-v2--disabled opacity-60' : '',
|
|
@@ -67,6 +82,7 @@ function select(option: PillTab) {
|
|
|
67
82
|
:aria-disabled="disabled || undefined"
|
|
68
83
|
>
|
|
69
84
|
<div
|
|
85
|
+
ref="listRef"
|
|
70
86
|
role="tablist"
|
|
71
87
|
:aria-label="ariaLabel"
|
|
72
88
|
class="pill-tabs-v2__list flex w-full flex-nowrap items-center gap-1 overflow-x-auto"
|
|
@@ -80,13 +96,13 @@ function select(option: PillTab) {
|
|
|
80
96
|
:aria-disabled="isDisabled(option) || undefined"
|
|
81
97
|
:disabled="isDisabled(option)"
|
|
82
98
|
:tabindex="modelValue === option.key ? 0 : -1"
|
|
83
|
-
class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-
|
|
99
|
+
class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-md px-3 py-1.5 font-medium transition-colors duration-150"
|
|
84
100
|
:class="[
|
|
85
101
|
SIZE_CLASS[size],
|
|
86
102
|
isDisabled(option)
|
|
87
103
|
? 'cursor-not-allowed text-gray-300 dark:text-gray-600'
|
|
88
104
|
: modelValue === option.key
|
|
89
|
-
? 'text-deepblue-900
|
|
105
|
+
? 'bg-white text-deepblue-900 shadow-sm dark:bg-gray-700 dark:text-gray-100 dark:shadow-none dark:ring-1 dark:ring-inset dark:ring-white/10'
|
|
90
106
|
: 'text-gray-700 hover:bg-white/70 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-white/[0.06] dark:hover:text-gray-100',
|
|
91
107
|
block ? 'flex-1 basis-0 justify-center' : 'shrink-0',
|
|
92
108
|
]"
|
|
@@ -97,7 +113,7 @@ function select(option: PillTab) {
|
|
|
97
113
|
|
|
98
114
|
<span
|
|
99
115
|
v-if="showCount && !option.hideCount"
|
|
100
|
-
class="inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-
|
|
116
|
+
class="inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-body-sm font-semibold leading-5"
|
|
101
117
|
:class="COUNT_COLOR_CLASS[option.countColor ?? 'gray']"
|
|
102
118
|
>
|
|
103
119
|
{{ option.count }}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onKeyStroke, useScrollLock } from '@vueuse/core'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<{
|
|
5
|
+
title?: string
|
|
6
|
+
closeOnOverlay?: boolean
|
|
7
|
+
closeOnEsc?: boolean
|
|
8
|
+
lockScroll?: boolean
|
|
9
|
+
persistent?: boolean
|
|
10
|
+
transition?: boolean
|
|
11
|
+
contentClass?: string
|
|
12
|
+
hideHeader?: boolean
|
|
13
|
+
blur?: boolean | 'sm' | 'md' | 'lg' | 'xl'
|
|
14
|
+
swipeToClose?: boolean
|
|
15
|
+
swipeThreshold?: number
|
|
16
|
+
showDragHandle?: boolean
|
|
17
|
+
maxHeight?: string
|
|
18
|
+
}>(), {
|
|
19
|
+
closeOnOverlay: true,
|
|
20
|
+
closeOnEsc: true,
|
|
21
|
+
lockScroll: true,
|
|
22
|
+
persistent: false,
|
|
23
|
+
transition: true,
|
|
24
|
+
hideHeader: false,
|
|
25
|
+
blur: false,
|
|
26
|
+
swipeToClose: true,
|
|
27
|
+
swipeThreshold: 65,
|
|
28
|
+
showDragHandle: true,
|
|
29
|
+
maxHeight: '100dvh',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const emit = defineEmits<{
|
|
33
|
+
(e: 'close'): void
|
|
34
|
+
(e: 'open'): void
|
|
35
|
+
}>()
|
|
36
|
+
|
|
37
|
+
const open = defineModel<boolean>({ required: true })
|
|
38
|
+
|
|
39
|
+
const contentRef = ref<HTMLElement | null>(null)
|
|
40
|
+
const overlayRef = ref<HTMLElement | null>(null)
|
|
41
|
+
const swipeClosing = ref(false)
|
|
42
|
+
const bodyScrollLocked = useScrollLock(import.meta.client ? document.body : null)
|
|
43
|
+
|
|
44
|
+
const blurClass = computed(() => {
|
|
45
|
+
if (!props.blur) return ''
|
|
46
|
+
|
|
47
|
+
switch (props.blur) {
|
|
48
|
+
case 'sm': return 'backdrop-blur-sm'
|
|
49
|
+
case 'md': return 'backdrop-blur-md'
|
|
50
|
+
case 'lg': return 'backdrop-blur-lg'
|
|
51
|
+
case 'xl': return 'backdrop-blur-xl'
|
|
52
|
+
default: return 'backdrop-blur'
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const overlayClass = computed(() => {
|
|
57
|
+
const base = ['drawer-v2-overlay fixed inset-0 z-[10000] flex items-end justify-stretch bg-black/40 dark:bg-black/60']
|
|
58
|
+
if (blurClass.value) base.push(blurClass.value)
|
|
59
|
+
return base.join(' ')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const contentStyle = computed(() => ({ maxHeight: props.maxHeight }))
|
|
63
|
+
|
|
64
|
+
const overlayTransitionName = computed(() => {
|
|
65
|
+
if (swipeClosing.value) return ''
|
|
66
|
+
return props.transition ? 'drawer-v2-overlay' : ''
|
|
67
|
+
})
|
|
68
|
+
const contentTransitionName = computed(() => {
|
|
69
|
+
if (swipeClosing.value || !props.transition) return ''
|
|
70
|
+
return 'drawer-v2-sheet'
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
function closeIfAllowed() {
|
|
74
|
+
if (props.persistent) return
|
|
75
|
+
open.value = false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function onOverlayClick(event: MouseEvent) {
|
|
79
|
+
if (!props.closeOnOverlay || props.persistent) return
|
|
80
|
+
if (event.target === event.currentTarget) {
|
|
81
|
+
open.value = false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onKeyStroke('Escape', () => {
|
|
86
|
+
if (!open.value) return
|
|
87
|
+
if (!props.closeOnEsc || props.persistent) return
|
|
88
|
+
open.value = false
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
function setScrollbarCompensation(active: boolean) {
|
|
92
|
+
if (!import.meta.client) return
|
|
93
|
+
|
|
94
|
+
if (active) {
|
|
95
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
|
|
96
|
+
document.body.style.paddingRight = scrollbarWidth > 0 ? `${scrollbarWidth}px` : ''
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
document.body.style.paddingRight = ''
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
watch(open, (value) => {
|
|
104
|
+
if (props.lockScroll) {
|
|
105
|
+
if (value) setScrollbarCompensation(true)
|
|
106
|
+
bodyScrollLocked.value = value
|
|
107
|
+
if (!value) setScrollbarCompensation(false)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (value) emit('open')
|
|
111
|
+
else emit('close')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
onBeforeUnmount(() => {
|
|
115
|
+
if (bodyScrollLocked.value) {
|
|
116
|
+
bodyScrollLocked.value = false
|
|
117
|
+
}
|
|
118
|
+
setScrollbarCompensation(false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const dragging = ref(false)
|
|
122
|
+
const dragStartY = ref(0)
|
|
123
|
+
const dragDelta = ref(0)
|
|
124
|
+
|
|
125
|
+
function canSwipe() {
|
|
126
|
+
return props.swipeToClose && !props.persistent
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function setTransform(value: string) {
|
|
130
|
+
if (contentRef.value) {
|
|
131
|
+
contentRef.value.style.transform = value
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function onTouchStart(event: TouchEvent) {
|
|
136
|
+
if (!canSwipe()) return
|
|
137
|
+
dragging.value = true
|
|
138
|
+
dragStartY.value = event.touches[0].clientY
|
|
139
|
+
dragDelta.value = 0
|
|
140
|
+
|
|
141
|
+
if (contentRef.value) {
|
|
142
|
+
contentRef.value.style.transition = 'none'
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onTouchMove(event: TouchEvent) {
|
|
147
|
+
if (!dragging.value || !canSwipe()) return
|
|
148
|
+
|
|
149
|
+
const delta = event.touches[0].clientY - dragStartY.value
|
|
150
|
+
if (delta < 0) {
|
|
151
|
+
dragDelta.value = 0
|
|
152
|
+
setTransform('')
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
dragDelta.value = delta
|
|
157
|
+
setTransform(`translateY(${delta}px)`)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function prefersReducedMotion() {
|
|
161
|
+
return import.meta.client && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function animateClose() {
|
|
165
|
+
const content = contentRef.value
|
|
166
|
+
|
|
167
|
+
if (!content || prefersReducedMotion()) {
|
|
168
|
+
open.value = false
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
swipeClosing.value = true
|
|
173
|
+
|
|
174
|
+
let done = false
|
|
175
|
+
const finish = () => {
|
|
176
|
+
if (done) return
|
|
177
|
+
done = true
|
|
178
|
+
content.removeEventListener('transitionend', onTransitionEnd)
|
|
179
|
+
open.value = false
|
|
180
|
+
swipeClosing.value = false
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function onTransitionEnd(event: TransitionEvent) {
|
|
184
|
+
if (event.target === content && event.propertyName === 'transform') {
|
|
185
|
+
finish()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
content.addEventListener('transitionend', onTransitionEnd)
|
|
190
|
+
setTimeout(finish, 320)
|
|
191
|
+
|
|
192
|
+
requestAnimationFrame(() => {
|
|
193
|
+
content.style.transition = 'transform 260ms cubic-bezier(0.22, 1, 0.36, 1)'
|
|
194
|
+
content.style.transform = 'translateY(100%)'
|
|
195
|
+
|
|
196
|
+
if (overlayRef.value) {
|
|
197
|
+
overlayRef.value.style.transition = 'opacity 260ms ease'
|
|
198
|
+
overlayRef.value.style.opacity = '0'
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function onTouchEnd() {
|
|
204
|
+
if (!dragging.value) return
|
|
205
|
+
dragging.value = false
|
|
206
|
+
|
|
207
|
+
const shouldClose = dragDelta.value > props.swipeThreshold && canSwipe()
|
|
208
|
+
|
|
209
|
+
if (shouldClose) {
|
|
210
|
+
animateClose()
|
|
211
|
+
}
|
|
212
|
+
else if (contentRef.value) {
|
|
213
|
+
contentRef.value.style.transition = 'transform 220ms cubic-bezier(0.22, 1, 0.36, 1)'
|
|
214
|
+
contentRef.value.style.transform = ''
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
dragDelta.value = 0
|
|
218
|
+
}
|
|
219
|
+
</script>
|
|
220
|
+
|
|
221
|
+
<template>
|
|
222
|
+
<client-only>
|
|
223
|
+
<teleport to="body">
|
|
224
|
+
<transition :name="overlayTransitionName">
|
|
225
|
+
<div
|
|
226
|
+
v-if="open"
|
|
227
|
+
ref="overlayRef"
|
|
228
|
+
:class="overlayClass"
|
|
229
|
+
@mousedown="onOverlayClick"
|
|
230
|
+
>
|
|
231
|
+
<transition :name="contentTransitionName" appear>
|
|
232
|
+
<div
|
|
233
|
+
v-if="open"
|
|
234
|
+
ref="contentRef"
|
|
235
|
+
class="drawer-v2-content flex w-full max-w-none flex-col overflow-hidden rounded-t-xl border-t border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-900"
|
|
236
|
+
:style="contentStyle"
|
|
237
|
+
@mousedown.stop
|
|
238
|
+
>
|
|
239
|
+
<div
|
|
240
|
+
v-if="showDragHandle"
|
|
241
|
+
class="flex shrink-0 cursor-grab touch-none justify-center py-2"
|
|
242
|
+
@touchstart.passive="onTouchStart"
|
|
243
|
+
@touchmove="onTouchMove"
|
|
244
|
+
@touchend="onTouchEnd"
|
|
245
|
+
@touchcancel="onTouchEnd"
|
|
246
|
+
>
|
|
247
|
+
<div class="h-1 w-10 rounded-full bg-gray-300 dark:bg-gray-600" />
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<header
|
|
251
|
+
v-if="!hideHeader && (title || $slots.header)"
|
|
252
|
+
class="flex shrink-0 items-center justify-between gap-3 border-b border-gray-200 px-5 py-4 dark:border-gray-800"
|
|
253
|
+
@touchstart.passive="onTouchStart"
|
|
254
|
+
@touchmove="onTouchMove"
|
|
255
|
+
@touchend="onTouchEnd"
|
|
256
|
+
@touchcancel="onTouchEnd"
|
|
257
|
+
>
|
|
258
|
+
<slot name="header">
|
|
259
|
+
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100">
|
|
260
|
+
{{ title }}
|
|
261
|
+
</h2>
|
|
262
|
+
<button
|
|
263
|
+
type="button"
|
|
264
|
+
class="rounded-sm p-1 text-gray-500 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
|
265
|
+
@click="closeIfAllowed"
|
|
266
|
+
>
|
|
267
|
+
<a-icon-x-mark class="size-4" />
|
|
268
|
+
</button>
|
|
269
|
+
</slot>
|
|
270
|
+
</header>
|
|
271
|
+
|
|
272
|
+
<div class="min-h-0 flex-1 overflow-auto" :class="contentClass">
|
|
273
|
+
<slot :close="closeIfAllowed" />
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<footer
|
|
277
|
+
v-if="$slots.footer"
|
|
278
|
+
class="shrink-0 border-t border-gray-200 px-5 py-4 dark:border-gray-800"
|
|
279
|
+
>
|
|
280
|
+
<slot name="footer" :close="closeIfAllowed" />
|
|
281
|
+
</footer>
|
|
282
|
+
</div>
|
|
283
|
+
</transition>
|
|
284
|
+
</div>
|
|
285
|
+
</transition>
|
|
286
|
+
</teleport>
|
|
287
|
+
</client-only>
|
|
288
|
+
</template>
|
|
289
|
+
|
|
290
|
+
<style scoped lang="scss">
|
|
291
|
+
.drawer-v2-overlay {
|
|
292
|
+
will-change: opacity;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.drawer-v2-content {
|
|
296
|
+
will-change: transform;
|
|
297
|
+
transform: translateZ(0);
|
|
298
|
+
backface-visibility: hidden;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.drawer-v2-overlay-enter-active {
|
|
302
|
+
transition: opacity 240ms ease-out;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.drawer-v2-overlay-leave-active {
|
|
306
|
+
transition: opacity 220ms ease-in;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.drawer-v2-overlay-enter-from,
|
|
310
|
+
.drawer-v2-overlay-leave-to {
|
|
311
|
+
opacity: 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.drawer-v2-sheet-enter-active {
|
|
315
|
+
transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.drawer-v2-sheet-leave-active {
|
|
319
|
+
transition: transform 240ms cubic-bezier(0.4, 0, 1, 1);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.drawer-v2-sheet-enter-from,
|
|
323
|
+
.drawer-v2-sheet-leave-to {
|
|
324
|
+
transform: translate3d(0, 100%, 0);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@media (prefers-reduced-motion: reduce) {
|
|
328
|
+
.drawer-v2-overlay-enter-active,
|
|
329
|
+
.drawer-v2-overlay-leave-active,
|
|
330
|
+
.drawer-v2-sheet-enter-active,
|
|
331
|
+
.drawer-v2-sheet-leave-active {
|
|
332
|
+
transition: none;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
|
|
3
|
-
import { twJoin, twMerge } from '
|
|
3
|
+
import { twJoin, twMerge } from '#adata-ui/utils/twMerge'
|
|
4
4
|
import { onClickOutside, useWindowSize } from '@vueuse/core'
|
|
5
5
|
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'
|
|
6
6
|
|
|
@@ -22,13 +22,13 @@ const uiConfig = {
|
|
|
22
22
|
position: 'top-1/2 left-1/2 translate-y-[-50%] translate-x-[-50%]',
|
|
23
23
|
padding: "p-8",
|
|
24
24
|
background: "bg-white dark:bg-gray-900",
|
|
25
|
-
rounded: "rounded-
|
|
25
|
+
rounded: "rounded-md",
|
|
26
26
|
fullscreen: "w-screen h-screen rounded-none",
|
|
27
27
|
mobile: {
|
|
28
28
|
width: "w-screen",
|
|
29
29
|
position: "bottom-0 left-0 transition-transform",
|
|
30
30
|
padding: 'px-4 sm:pb-8 pb-4 pt-3',
|
|
31
|
-
rounded: "rounded-t-
|
|
31
|
+
rounded: "rounded-t-xl"
|
|
32
32
|
},
|
|
33
33
|
transition: {
|
|
34
34
|
desktop: {
|
|
@@ -297,7 +297,7 @@ watch(modelValue, async (newValue) => {
|
|
|
297
297
|
>
|
|
298
298
|
<h4
|
|
299
299
|
v-if="title"
|
|
300
|
-
class="
|
|
300
|
+
class="text-h2 text-center"
|
|
301
301
|
@touchmove="touchmove"
|
|
302
302
|
@touchstart="touchstart"
|
|
303
303
|
@touchend="touchend"
|
|
@@ -14,6 +14,7 @@ const props = withDefaults(defineProps<{
|
|
|
14
14
|
transition?: boolean
|
|
15
15
|
contentClass?: string
|
|
16
16
|
hideHeader?: boolean
|
|
17
|
+
hideClose?: boolean
|
|
17
18
|
blur?: boolean | 'sm' | 'md' | 'lg' | 'xl'
|
|
18
19
|
mobileSheet?: boolean
|
|
19
20
|
swipeToClose?: boolean
|
|
@@ -27,6 +28,7 @@ const props = withDefaults(defineProps<{
|
|
|
27
28
|
persistent: false,
|
|
28
29
|
transition: true,
|
|
29
30
|
hideHeader: false,
|
|
31
|
+
hideClose: false,
|
|
30
32
|
blur: false,
|
|
31
33
|
mobileSheet: true,
|
|
32
34
|
swipeToClose: true,
|
|
@@ -96,10 +98,10 @@ const contentBaseClass = computed(() => {
|
|
|
96
98
|
const base = ['modal-v2-content flex flex-col overflow-hidden border bg-white shadow-2xl dark:bg-gray-900']
|
|
97
99
|
|
|
98
100
|
if (isSheet.value) {
|
|
99
|
-
base.push('w-full max-w-none max-h-[100dvh] rounded-t-
|
|
101
|
+
base.push('w-full max-w-none max-h-[100dvh] rounded-t-xl rounded-b-none border-x-0 border-b-0 border-t border-gray-200 dark:border-gray-800')
|
|
100
102
|
}
|
|
101
103
|
else {
|
|
102
|
-
const radius = props.size === 'fullscreen' ? 'rounded-none' : 'rounded-
|
|
104
|
+
const radius = props.size === 'fullscreen' ? 'rounded-none' : 'rounded-xl'
|
|
103
105
|
const widthCls = props.size === 'container'
|
|
104
106
|
? ' w-full max-w-screen-sm md:max-w-screen-md lg:max-w-screen-lg xl:max-w-screen-xl 2xl:max-w-screen-2xl'
|
|
105
107
|
: ''
|
|
@@ -306,13 +308,14 @@ function onTouchEnd() {
|
|
|
306
308
|
@touchcancel="onTouchEnd"
|
|
307
309
|
>
|
|
308
310
|
<slot name="header">
|
|
309
|
-
<h2 v-if="title" class="text-
|
|
311
|
+
<h2 v-if="title" class="text-h5 text-gray-900 dark:text-gray-100">
|
|
310
312
|
{{ title }}
|
|
311
313
|
</h2>
|
|
312
314
|
</slot>
|
|
313
315
|
<button
|
|
316
|
+
v-if="!hideClose"
|
|
314
317
|
type="button"
|
|
315
|
-
class="ml-auto rounded-
|
|
318
|
+
class="ml-auto rounded-sm p-1 transition hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
|
316
319
|
@click="closeIfAllowed"
|
|
317
320
|
>
|
|
318
321
|
<a-icon-x-mark class="size-4" />
|
|
@@ -128,10 +128,10 @@ watch(() => [modelValue.value, panel.value], (newP, oldP) => {
|
|
|
128
128
|
|
|
129
129
|
const roundedClass = computed(() => {
|
|
130
130
|
const classes = {
|
|
131
|
-
left: "rounded-r-
|
|
132
|
-
right: "rounded-l-
|
|
133
|
-
top: "rounded-b-
|
|
134
|
-
bottom: "rounded-t-
|
|
131
|
+
left: "rounded-r-lg",
|
|
132
|
+
right: "rounded-l-lg",
|
|
133
|
+
top: "rounded-b-lg",
|
|
134
|
+
bottom: "rounded-t-lg",
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
return classes[panelSide.value]
|
|
@@ -170,7 +170,7 @@ const modalHeight = ref(0)
|
|
|
170
170
|
const touchstart = (event: TouchEvent) => {
|
|
171
171
|
isClose.value = false
|
|
172
172
|
positionStart.value = event.touches[0].clientY
|
|
173
|
-
modalHeight.value = panel.value?.offsetHeight || 0
|
|
173
|
+
modalHeight.value = panel.value?.offsetHeight || 0
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
const touchmove = (event: TouchEvent) => {
|
|
@@ -179,7 +179,7 @@ const touchmove = (event: TouchEvent) => {
|
|
|
179
179
|
const touchPosition = event.touches[0].clientY
|
|
180
180
|
const step = touchPosition - positionStart.value
|
|
181
181
|
|
|
182
|
-
if (step < 0) return
|
|
182
|
+
if (step < 0) return
|
|
183
183
|
|
|
184
184
|
const newHeight = modalHeight.value - step
|
|
185
185
|
panel.value.style.height = `${newHeight}px`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
|
|
3
|
-
import { twJoin, twMerge } from '
|
|
3
|
+
import { twJoin, twMerge } from '#adata-ui/utils/twMerge'
|
|
4
4
|
import { onClickOutside, useWindowSize } from '@vueuse/core'
|
|
5
5
|
import {disableBodyScroll, enableBodyScroll} from "body-scroll-lock";
|
|
6
6
|
|
|
@@ -37,10 +37,10 @@ const uiConfig = {
|
|
|
37
37
|
bottom: 'bottom-0 left-0 w-full'
|
|
38
38
|
},
|
|
39
39
|
rounded: {
|
|
40
|
-
top: 'rounded-b-
|
|
41
|
-
bottom: 'rounded-t-
|
|
42
|
-
left: 'rounded-r-
|
|
43
|
-
right: 'rounded-l-
|
|
40
|
+
top: 'rounded-b-md',
|
|
41
|
+
bottom: 'rounded-t-md',
|
|
42
|
+
left: 'rounded-r-md',
|
|
43
|
+
right: 'rounded-l-md'
|
|
44
44
|
},
|
|
45
45
|
fullscreen: 'w-screen h-screen rounded-none',
|
|
46
46
|
transition: {
|
|
@@ -219,7 +219,7 @@ const modalHeight = ref(0)
|
|
|
219
219
|
const touchstart = (event: TouchEvent) => {
|
|
220
220
|
isClose.value = false
|
|
221
221
|
positionStart.value = event.touches[0].clientY
|
|
222
|
-
modalHeight.value = slideover.value?.offsetHeight || 0
|
|
222
|
+
modalHeight.value = slideover.value?.offsetHeight || 0
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
const touchmove = (event: TouchEvent) => {
|
|
@@ -56,7 +56,7 @@ const baseClass = computed(() => {
|
|
|
56
56
|
const color = props.errorParsing ? 'text-[#E74135] dark:text-[#F47E75]' : 'text-white dark:text-deepblue-900'
|
|
57
57
|
const pad = props.size === 'sm' ? 'px-4 py-2' : 'px-4 py-4'
|
|
58
58
|
const truncated = props.truncate ? 'truncate' : 'max-w-[530px] w-max'
|
|
59
|
-
return ['text-
|
|
59
|
+
return ['text-body-sm font-normal absolute', bg, color, 'rounded-sm', pad, truncated].join(' ')
|
|
60
60
|
})
|
|
61
61
|
|
|
62
62
|
/* --- positioning logic --- */
|
|
@@ -9,27 +9,20 @@ import {
|
|
|
9
9
|
useFloating,
|
|
10
10
|
} from '@floating-ui/vue'
|
|
11
11
|
import { onClickOutside, onKeyStroke } from '@vueuse/core'
|
|
12
|
-
import { twMerge } from '
|
|
12
|
+
import { twMerge } from '#adata-ui/utils/twMerge'
|
|
13
13
|
import { useTooltipTrigger } from './useTooltipTrigger'
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
16
|
-
/** Preferred side; auto-flips when there isn't room. */
|
|
17
16
|
placement?: TooltipPlacement
|
|
18
|
-
/** How the tooltip opens. `manual` is fully driven by `v-model:open`. */
|
|
19
17
|
trigger?: TooltipTrigger
|
|
20
|
-
/** Show a pointer arrow aimed at the trigger. */
|
|
21
18
|
arrow?: boolean
|
|
22
|
-
/** Gap between trigger and tooltip, in px. */
|
|
23
19
|
offset?: number
|
|
24
20
|
openDelay?: number
|
|
25
21
|
closeDelay?: number
|
|
26
|
-
/** Render through `<Teleport to="body">` to escape overflow/stacking contexts. */
|
|
27
22
|
teleport?: boolean
|
|
28
23
|
disabled?: boolean
|
|
29
24
|
zIndex?: number
|
|
30
|
-
/** Plain-text fallback used when the `content` slot is empty. */
|
|
31
25
|
text?: string
|
|
32
|
-
/** Extra classes merged onto the tooltip box (e.g. width/padding overrides). */
|
|
33
26
|
contentClass?: string
|
|
34
27
|
}
|
|
35
28
|
|
|
@@ -80,7 +73,6 @@ const { x, y, strategy, middlewareData, placement: resolvedPlacement } = useFloa
|
|
|
80
73
|
const side = computed(() => resolvedPlacement.value.split('-')[0])
|
|
81
74
|
const opposite: Record<string, string> = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' }
|
|
82
75
|
|
|
83
|
-
// Grow the panel out of the edge nearest the trigger.
|
|
84
76
|
const transformOrigin = computed(() => {
|
|
85
77
|
const [main, align] = resolvedPlacement.value.split('-')
|
|
86
78
|
const anchor = opposite[main] ?? 'center'
|
|
@@ -92,7 +84,6 @@ const transformOrigin = computed(() => {
|
|
|
92
84
|
return `${h} ${anchor}`
|
|
93
85
|
})
|
|
94
86
|
|
|
95
|
-
// The two outer edges of the rotated square form the visible point.
|
|
96
87
|
const arrowBorderClass = computed(() => {
|
|
97
88
|
switch (side.value) {
|
|
98
89
|
case 'top': return 'border-b border-r'
|
|
@@ -117,12 +108,11 @@ const interactive = computed(() => props.trigger !== 'hover')
|
|
|
117
108
|
|
|
118
109
|
const boxClass = computed(() =>
|
|
119
110
|
twMerge(
|
|
120
|
-
'rounded-
|
|
111
|
+
'rounded-md bg-white px-2.5 py-1.5 shadow-lg ring-1 ring-gray-200 dark:bg-gray-900 dark:ring-gray-700',
|
|
121
112
|
props.contentClass,
|
|
122
113
|
),
|
|
123
114
|
)
|
|
124
115
|
|
|
125
|
-
// Click mode dismisses on outside-click / Escape; hover leaves on its own; manual is parent-driven.
|
|
126
116
|
onClickOutside(
|
|
127
117
|
floating,
|
|
128
118
|
() => {
|