adata-ui 2.1.40-beta.18 → 2.1.40-beta.19
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/.playground/app.vue +102 -0
- package/assets/styles/index.scss +67 -5
- package/components/elements/a-select-row/ASelectRowV2.vue +214 -0
- 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 +133 -0
- package/components/elements/button-login/index.vue +6 -10
- package/components/elements/chip-wrapper/AChipWrapper.vue +1 -1
- package/components/elements/chips/AChips.vue +2 -2
- package/components/elements/companies/AOurClients.vue +3 -1
- package/components/elements/curve-block-v2/ACurveBlockV2.vue +85 -0
- package/components/elements/digit-badge/ADigitBadge.vue +24 -22
- package/components/elements/feature-description/AFeatureDescription.vue +1 -1
- package/components/elements/leave-note/ALeaveNote.vue +39 -18
- package/components/elements/pagination/APagination.vue +7 -7
- package/components/elements/segmented/ASegmentedV2.vue +151 -0
- package/components/elements/select/ASelect.vue +4 -4
- package/components/elements/select/ASelectMobile.vue +2 -2
- package/components/elements/select/ASelectV2.vue +588 -0
- package/components/elements/select/ui/select-button.vue +1 -1
- package/components/elements/show-more/AShowMoreV2.vue +26 -0
- package/components/elements/slider/ASlider.vue +174 -0
- package/components/elements/slider/ASliderItem.vue +31 -0
- package/components/elements/slider/types.ts +21 -0
- package/components/elements/star-rating/AStarRating.vue +12 -4
- package/components/elements/status-badge/AStatusBadge.vue +91 -44
- package/components/elements/step-slider/AStepSliderColCard.vue +232 -0
- package/components/elements/step-slider/AStepSliderColNumber.vue +178 -0
- 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 +74 -32
- package/components/features/dropdown/ADropdownV2.vue +140 -0
- package/components/features/lang-switcher/lang-switcher.vue +125 -40
- 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 +45 -27
- package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +34 -23
- package/components/features/payment/process/PaymentMethodSidePanel.vue +66 -49
- package/components/features/payment/process/PaymentProcess.vue +6 -6
- package/components/features/payment/process/PaymentTopUpSidePanel.vue +49 -63
- package/components/features/pk-mobile-services/APkMobileServices.vue +6 -28
- package/components/features//321/201hange-version/AChangeVersion.vue +2 -2
- package/components/forms/checkbox/ACheckbox.vue +1 -1
- package/components/forms/checkbox/ACheckboxV2.vue +229 -0
- package/components/forms/feedback-form/FeedbackForm.vue +8 -8
- package/components/forms/feedback-form/FeedbackFormV2.vue +182 -0
- package/components/forms/input/AInputV2.vue +542 -0
- 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/input/textarea/ATextareaV2.vue +248 -0
- package/components/forms/toggle/AToggle.vue +2 -3
- package/components/forms/toggle/AToggleV2.vue +71 -0
- package/components/modals/AnotherDeviceModal.vue +22 -18
- package/components/modals/ApplicationAcceptedModal.vue +12 -15
- package/components/modals/ConnectingTariffModal.vue +13 -20
- package/components/modals/ContactsMobileModel.vue +1 -1
- package/components/modals/ContentNavigationModal.vue +77 -333
- package/components/modals/Insufficient-funds-modal.vue +18 -33
- package/components/modals/LimitReachedModal.vue +17 -14
- package/components/modals/NoAccessModal.vue +9 -11
- package/components/modals/PaymentMethodModal.vue +1 -1
- package/components/modals/ReportBugConfirmModal.vue +12 -10
- package/components/modals/ReportBugModal.vue +46 -38
- package/components/modals/Resend.vue +9 -10
- package/components/modals/SubmitApplicationModal.vue +27 -24
- package/components/modals/id/IdBanner.vue +3 -3
- package/components/modals/id/IdConfirmAccountOtpModal.vue +4 -9
- package/components/modals/id/IdLoginModal.vue +3 -8
- package/components/modals/id/IdModals.vue +23 -10
- package/components/modals/id/IdRecoveryModal.vue +1 -1
- package/components/modals/id/IdTwoFactorModal.vue +3 -8
- 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 +68 -51
- package/components/navigation/header/AlmatyContacts.vue +78 -78
- package/components/navigation/header/CardGallery.vue +9 -7
- package/components/navigation/header/ContactMenu.vue +26 -92
- package/components/navigation/header/HeaderLink.vue +181 -213
- package/components/navigation/header/HeaderUsage.vue +139 -0
- package/components/navigation/header/ListItem.vue +2 -2
- package/components/navigation/header/NavCard.vue +1 -1
- package/components/navigation/header/NavList.vue +33 -92
- package/components/navigation/header/NotificationsMenu.vue +2 -3
- package/components/navigation/header/ProductMenu.vue +81 -127
- package/components/navigation/header/ProfileMenu.vue +122 -151
- package/components/navigation/header/SystemNotification.vue +108 -0
- package/components/navigation/mobile-navigation/AMobileNavigation.vue +23 -15
- package/components/navigation/pill-tabs/APillTabs.vue +2 -2
- package/components/navigation/pill-tabs/APillTabsV2.vue +226 -0
- package/components/overlays/drawer/ADrawerV2.vue +335 -0
- package/components/overlays/modal/AModal.vue +4 -4
- package/components/overlays/modal/AModalV2.vue +397 -0
- package/components/overlays/side-panel/ASidePanel.vue +10 -7
- package/components/overlays/sideover/ASlideover.vue +6 -6
- package/components/overlays/tooltip/ATooltip.vue +1 -1
- package/components/overlays/tooltip/ATooltipV2.vue +223 -0
- package/components/overlays/tooltip/types.ts +26 -0
- package/components/overlays/tooltip/useTooltipTrigger.ts +101 -0
- package/components/transitions/ATransitionHeight.vue +1 -1
- package/composables/useAccessTokenCookie.ts +25 -0
- package/composables/useActiveNavigation.ts +84 -0
- package/composables/useChipOverflow.ts +92 -0
- package/composables/useHeaderNavigationLinks.ts +140 -98
- package/icons/archive.vue +16 -0
- package/icons/arrow/arrow-left-right.vue +8 -0
- package/icons/arrow/arrow-right.vue +14 -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/bill.vue +3 -0
- package/icons/bot.vue +52 -0
- package/icons/briefcase.vue +14 -0
- package/icons/building.vue +9 -0
- package/icons/car-side.vue +16 -0
- package/icons/cart.vue +15 -0
- package/icons/chart-no-axes-column.vue +3 -0
- package/icons/clock-time.vue +24 -0
- package/icons/cloud/cloud-upload.vue +3 -0
- package/icons/code.vue +9 -0
- package/icons/compare-outlined.vue +8 -0
- package/icons/dots-horizontal.vue +31 -0
- package/icons/download-line.vue +13 -0
- package/icons/download-rounded.vue +15 -0
- package/icons/egov.vue +41 -0
- package/icons/elicense.vue +38 -0
- package/icons/favourite-bookmark-outline.vue +17 -0
- package/icons/file/file-check-corner.vue +3 -0
- package/icons/file/file-spreadsheet.vue +3 -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/gauge.vue +17 -0
- package/icons/git-compare.vue +38 -0
- package/icons/git-fork.vue +3 -0
- package/icons/graduation-cap.vue +31 -0
- package/icons/graph-loading.vue +34 -0
- package/icons/headset.vue +6 -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/landmark.vue +3 -0
- package/icons/layers.vue +3 -0
- package/icons/letter/letter-a.vue +6 -0
- package/icons/letter/letter-b.vue +6 -0
- package/icons/letter/letter-c.vue +6 -0
- package/icons/letter/letter-d.vue +6 -0
- package/icons/letter/letter-e.vue +6 -0
- package/icons/link-url.vue +3 -0
- package/icons/list-tree.vue +3 -0
- package/icons/mail-question-mark.vue +8 -0
- package/icons/message/message-square.vue +3 -0
- package/icons/message-dotted.vue +30 -0
- package/icons/network.vue +3 -0
- package/icons/newspaper.vue +3 -0
- package/icons/notification-error.vue +17 -0
- package/icons/person.vue +22 -0
- package/icons/phone-call.vue +6 -0
- package/icons/phone-reversed.vue +3 -0
- package/icons/route.vue +7 -0
- package/icons/scan-search.vue +3 -0
- package/icons/settings.vue +26 -0
- package/icons/shield-alert.vue +31 -0
- package/icons/shield-verified.vue +14 -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/sparkles.vue +3 -0
- package/icons/sun.vue +13 -3
- package/icons/users-round.vue +3 -0
- package/icons/view-eye-open.vue +13 -0
- package/icons/workflow.vue +3 -0
- package/icons/zap.vue +3 -0
- package/lang/en.ts +49 -5
- package/lang/kk.ts +50 -6
- package/lang/ru.ts +51 -7
- package/package.json +1 -1
- package/plugins/toast.client.ts +38 -55
- package/shared/constans/pages.ts +8 -2
- package/tailwind.config.ts +147 -116
- package/utils/twMerge.ts +34 -0
- package/components/elements/tree-select/components/tree-select-nodes.vue +0 -91
- package/components/navigation/header/TopHeader.vue +0 -196
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useDocumentVisibility, useElementHover } from '@vueuse/core'
|
|
3
|
+
import { computed, provide, ref } from 'vue'
|
|
4
|
+
import { SLIDER_CONTEXT } from './types'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Time each slide stays active, in ms. */
|
|
8
|
+
interval?: number
|
|
9
|
+
/** Advance automatically. */
|
|
10
|
+
autoplay?: boolean
|
|
11
|
+
/** Wrap around from the last slide back to the first. */
|
|
12
|
+
loop?: boolean
|
|
13
|
+
/** Freeze autoplay while the cursor is over the slider. */
|
|
14
|
+
pauseOnHover?: boolean
|
|
15
|
+
/** Freeze autoplay while the browser tab is hidden. */
|
|
16
|
+
pauseOnHidden?: boolean
|
|
17
|
+
/** Render the built-in progress indicators. */
|
|
18
|
+
indicators?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
interval: 5000,
|
|
23
|
+
autoplay: true,
|
|
24
|
+
loop: true,
|
|
25
|
+
pauseOnHover: true,
|
|
26
|
+
pauseOnHidden: true,
|
|
27
|
+
indicators: true,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/** Active slide index — usable as `v-model:index`. */
|
|
31
|
+
const index = defineModel<number>('index', { default: 0 })
|
|
32
|
+
|
|
33
|
+
const root = ref<HTMLElement>()
|
|
34
|
+
|
|
35
|
+
/* ---- item registration (drives `count` and per-item index) ---- */
|
|
36
|
+
const ids = ref<symbol[]>([])
|
|
37
|
+
const count = computed(() => ids.value.length)
|
|
38
|
+
|
|
39
|
+
function register(id: symbol) {
|
|
40
|
+
ids.value.push(id)
|
|
41
|
+
return computed(() => ids.value.indexOf(id))
|
|
42
|
+
}
|
|
43
|
+
function unregister(id: symbol) {
|
|
44
|
+
const i = ids.value.indexOf(id)
|
|
45
|
+
if (i !== -1)
|
|
46
|
+
ids.value.splice(i, 1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ---- navigation ---- */
|
|
50
|
+
const direction = ref<1 | -1>(1)
|
|
51
|
+
// Bumping this key remounts the clock/progress element, restarting the timing.
|
|
52
|
+
const tick = ref(0)
|
|
53
|
+
|
|
54
|
+
function go(to: number, dir?: 1 | -1) {
|
|
55
|
+
if (count.value === 0)
|
|
56
|
+
return
|
|
57
|
+
const n = ((to % count.value) + count.value) % count.value
|
|
58
|
+
direction.value = dir ?? (n >= index.value ? 1 : -1)
|
|
59
|
+
index.value = n
|
|
60
|
+
tick.value++
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function next() {
|
|
64
|
+
if (!props.loop && index.value >= count.value - 1)
|
|
65
|
+
return
|
|
66
|
+
go(index.value + 1, 1)
|
|
67
|
+
}
|
|
68
|
+
function prev() {
|
|
69
|
+
if (!props.loop && index.value <= 0)
|
|
70
|
+
return
|
|
71
|
+
go(index.value - 1, -1)
|
|
72
|
+
}
|
|
73
|
+
function goTo(i: number) {
|
|
74
|
+
go(i)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ---- autoplay ----
|
|
78
|
+
* Timing is driven by an invisible "clock" element's CSS animation.
|
|
79
|
+
* `animation-play-state: paused` freezes both the clock and the visible
|
|
80
|
+
* progress bar in one place, so hover/hidden pause stays perfectly in sync
|
|
81
|
+
* and resumes from where it stopped — no JS timers to keep aligned. */
|
|
82
|
+
const hovered = useElementHover(root)
|
|
83
|
+
const visibility = useDocumentVisibility()
|
|
84
|
+
|
|
85
|
+
const shouldRun = computed(() => props.autoplay && count.value > 1)
|
|
86
|
+
const paused = computed(() =>
|
|
87
|
+
(props.pauseOnHover && hovered.value)
|
|
88
|
+
|| (props.pauseOnHidden && visibility.value === 'hidden'),
|
|
89
|
+
)
|
|
90
|
+
const timingStyle = computed(() => ({
|
|
91
|
+
animationDuration: `${props.interval}ms`,
|
|
92
|
+
animationPlayState: paused.value ? 'paused' : 'running',
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
provide(SLIDER_CONTEXT, {
|
|
96
|
+
index,
|
|
97
|
+
count,
|
|
98
|
+
direction,
|
|
99
|
+
register,
|
|
100
|
+
unregister,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
defineExpose({ next, prev, goTo, index, count })
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
<template>
|
|
107
|
+
<div
|
|
108
|
+
ref="root"
|
|
109
|
+
class="relative overflow-hidden"
|
|
110
|
+
role="group"
|
|
111
|
+
aria-roledescription="carousel"
|
|
112
|
+
>
|
|
113
|
+
<!-- Invisible clock: fires `next` when a slide's time elapses. -->
|
|
114
|
+
<div
|
|
115
|
+
v-if="shouldRun"
|
|
116
|
+
:key="tick"
|
|
117
|
+
class="a-slider-clock"
|
|
118
|
+
:style="timingStyle"
|
|
119
|
+
@animationend="next"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<slot />
|
|
123
|
+
|
|
124
|
+
<div
|
|
125
|
+
v-if="indicators && count > 1"
|
|
126
|
+
class="absolute bottom-0 left-1/2 z-10 flex -translate-x-1/2 gap-2"
|
|
127
|
+
role="tablist"
|
|
128
|
+
>
|
|
129
|
+
<button
|
|
130
|
+
v-for="(_, i) in count"
|
|
131
|
+
:key="i"
|
|
132
|
+
type="button"
|
|
133
|
+
role="tab"
|
|
134
|
+
:aria-selected="index === i"
|
|
135
|
+
:aria-label="`Слайд ${i + 1}`"
|
|
136
|
+
class="relative h-[6px] cursor-pointer overflow-hidden rounded-full bg-blue-200 transition-all duration-300 dark:bg-blue-900/40"
|
|
137
|
+
:class="index === i ? 'w-[30px]' : 'w-[20px]'"
|
|
138
|
+
@click="goTo(i)"
|
|
139
|
+
>
|
|
140
|
+
<span
|
|
141
|
+
v-if="index === i && shouldRun"
|
|
142
|
+
:key="tick"
|
|
143
|
+
class="a-slider-progress absolute inset-0 rounded-full bg-blue-500 dark:bg-blue-400"
|
|
144
|
+
:style="timingStyle"
|
|
145
|
+
/>
|
|
146
|
+
</button>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</template>
|
|
150
|
+
|
|
151
|
+
<style scoped>
|
|
152
|
+
.a-slider-clock {
|
|
153
|
+
position: absolute;
|
|
154
|
+
width: 1px;
|
|
155
|
+
height: 1px;
|
|
156
|
+
opacity: 0;
|
|
157
|
+
pointer-events: none;
|
|
158
|
+
animation: a-slider-tick linear forwards;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.a-slider-progress {
|
|
162
|
+
animation: a-slider-progress linear forwards;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@keyframes a-slider-tick {
|
|
166
|
+
from { transform: scaleX(0); }
|
|
167
|
+
to { transform: scaleX(1); }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@keyframes a-slider-progress {
|
|
171
|
+
from { width: 0; }
|
|
172
|
+
to { width: 100%; }
|
|
173
|
+
}
|
|
174
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject, onBeforeUnmount } from 'vue'
|
|
3
|
+
import { SLIDER_CONTEXT } from './types'
|
|
4
|
+
|
|
5
|
+
const ctx = inject(SLIDER_CONTEXT)
|
|
6
|
+
if (!ctx)
|
|
7
|
+
throw new Error('<ASliderItem> must be used inside <ASlider>')
|
|
8
|
+
|
|
9
|
+
// Registered synchronously during setup, so siblings keep their template order.
|
|
10
|
+
const id = Symbol('a-slider-item')
|
|
11
|
+
const index = ctx.register(id)
|
|
12
|
+
onBeforeUnmount(() => ctx.unregister(id))
|
|
13
|
+
|
|
14
|
+
const active = computed(() => ctx.index.value === index.value)
|
|
15
|
+
const direction = computed(() => ctx.direction.value)
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<!-- Slides are stacked on the same stage so leave/enter animations overlap. -->
|
|
20
|
+
<div class="absolute inset-0">
|
|
21
|
+
<!--
|
|
22
|
+
The slider only tells the slide whether it's active and in which
|
|
23
|
+
direction. The slide owns its own <Transition> / animations.
|
|
24
|
+
-->
|
|
25
|
+
<slot
|
|
26
|
+
:active="active"
|
|
27
|
+
:index="index"
|
|
28
|
+
:direction="direction"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Context provided by <ASlider> and consumed by <ASliderItem>.
|
|
5
|
+
* The slider owns the mechanism (active index, count, direction);
|
|
6
|
+
* items only read from it and render their own animations.
|
|
7
|
+
*/
|
|
8
|
+
export interface SliderContext {
|
|
9
|
+
/** Index of the currently active slide. */
|
|
10
|
+
index: Readonly<Ref<number>>
|
|
11
|
+
/** Total number of registered slides. */
|
|
12
|
+
count: Readonly<Ref<number>>
|
|
13
|
+
/** Direction of the last transition: 1 = forward, -1 = backward. */
|
|
14
|
+
direction: Readonly<Ref<1 | -1>>
|
|
15
|
+
/** Register an item and get back its live index. */
|
|
16
|
+
register: (id: symbol) => ComputedRef<number>
|
|
17
|
+
/** Unregister an item on unmount. */
|
|
18
|
+
unregister: (id: symbol) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const SLIDER_CONTEXT: InjectionKey<SliderContext> = Symbol('a-slider')
|
|
@@ -18,6 +18,8 @@ interface Props {
|
|
|
18
18
|
fontSize?: number;
|
|
19
19
|
error?: string | boolean;
|
|
20
20
|
errorSize?: 'sm' | 'lg';
|
|
21
|
+
hideValue?: boolean;
|
|
22
|
+
hideEmptyStars?: boolean;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -32,7 +34,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
32
34
|
ratingPlacement: 'left',
|
|
33
35
|
fontSize: 14,
|
|
34
36
|
error: false,
|
|
35
|
-
errorSize: 'lg'
|
|
37
|
+
errorSize: 'lg',
|
|
38
|
+
hideValue: false,
|
|
39
|
+
hideEmptyStars: false
|
|
36
40
|
})
|
|
37
41
|
|
|
38
42
|
const fillLevel = ref<number[]>([])
|
|
@@ -51,6 +55,10 @@ const roundedRating = computed(() => {
|
|
|
51
55
|
})
|
|
52
56
|
const shouldRound = computed(() => ratingSelected.value || props.roundStartRating)
|
|
53
57
|
|
|
58
|
+
const visibleStarsCount = computed(() => {
|
|
59
|
+
if (!props.hideEmptyStars) return props.max
|
|
60
|
+
return Math.min(props.max, Math.max(1, Math.ceil(currentRating.value)))
|
|
61
|
+
})
|
|
54
62
|
|
|
55
63
|
watch(modelValue, (e)=>{
|
|
56
64
|
currentRating.value = e
|
|
@@ -105,7 +113,7 @@ createStars(shouldRound.value)
|
|
|
105
113
|
<div>
|
|
106
114
|
<div class="w-full flex items-center gap-2">
|
|
107
115
|
<span
|
|
108
|
-
v-if="ratingPlacement === 'left'"
|
|
116
|
+
v-if="ratingPlacement === 'left' && !hideValue"
|
|
109
117
|
class="text-gray-600 dark:text-gray-300 mt-px"
|
|
110
118
|
:style="{fontSize: `${fontSize}px`}"
|
|
111
119
|
>
|
|
@@ -118,7 +126,7 @@ createStars(shouldRound.value)
|
|
|
118
126
|
@mouseleave="resetRating"
|
|
119
127
|
>
|
|
120
128
|
<star
|
|
121
|
-
v-for="n in
|
|
129
|
+
v-for="n in visibleStarsCount"
|
|
122
130
|
:key="n"
|
|
123
131
|
:fill="fillLevel[n-1]"
|
|
124
132
|
:size="starSize"
|
|
@@ -127,7 +135,7 @@ createStars(shouldRound.value)
|
|
|
127
135
|
/>
|
|
128
136
|
</div>
|
|
129
137
|
<span
|
|
130
|
-
v-if="ratingPlacement === 'right'"
|
|
138
|
+
v-if="ratingPlacement === 'right' && !hideValue"
|
|
131
139
|
class="text-sm text-gray-600 dark:text-gray-300 mt-px"
|
|
132
140
|
>
|
|
133
141
|
{{ modelValue }}
|
|
@@ -1,74 +1,121 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
|
|
2
|
+
type BadgeType = 'primary' | 'success' | 'danger' | 'gray' | 'warning'
|
|
3
|
+
type BadgeView = 'default' | 'inverted' | 'transparent' | 'subtle'
|
|
4
|
+
type BadgeSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
type StateType = {
|
|
7
|
+
[key in BadgeType]?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ViewType {
|
|
11
|
+
default: StateType
|
|
12
|
+
inverted: StateType
|
|
13
|
+
transparent: StateType
|
|
14
|
+
subtle: StateType
|
|
15
|
+
}
|
|
5
16
|
|
|
6
17
|
interface Props {
|
|
7
|
-
type?:
|
|
8
|
-
view?:
|
|
9
|
-
size?:
|
|
18
|
+
type?: BadgeType
|
|
19
|
+
view?: BadgeView
|
|
20
|
+
size?: BadgeSize
|
|
21
|
+
point?: boolean
|
|
10
22
|
customClasses?: string
|
|
11
23
|
}
|
|
12
24
|
|
|
25
|
+
defineOptions({ name: 'AStatusBadge' })
|
|
26
|
+
|
|
13
27
|
const props = withDefaults(defineProps<Props>(), {
|
|
14
28
|
type: 'primary',
|
|
15
29
|
view: 'default',
|
|
16
30
|
size: 'lg',
|
|
17
|
-
|
|
31
|
+
point: false,
|
|
32
|
+
customClasses: '',
|
|
18
33
|
})
|
|
19
34
|
|
|
20
|
-
const classes = computed(() =>
|
|
21
|
-
[
|
|
22
|
-
'text-sm rounded-md select-none inline-flex gap-[5.5px] justify-center items-center',
|
|
23
|
-
typeSwitchValues[props.view][props.type],
|
|
24
|
-
props.size === 'xs'? 'px-4 py-[2px] text-xs font-bold' : '',
|
|
25
|
-
props.size === 'sm'? 'text-xs font-semibold px-4 py-[2px] h-6' : '',
|
|
26
|
-
props.size === 'lg'? 'px-4 py-[7.5px] font-semibold' : '',
|
|
27
|
-
])
|
|
28
|
-
|
|
29
|
-
|
|
30
35
|
const typeSwitchValues: ViewType = {
|
|
31
36
|
default: {
|
|
32
|
-
primary: 'bg-
|
|
33
|
-
success: 'bg-
|
|
34
|
-
danger: 'bg-
|
|
35
|
-
warning: 'bg-
|
|
36
|
-
gray: 'bg-
|
|
37
|
-
orange: 'bg-orange-600 text-white dark:text-gray-200'
|
|
37
|
+
primary: 'bg-[#068DFF] text-white border-transparent dark:bg-[#068DFF]/30 dark:text-blue-100',
|
|
38
|
+
success: 'bg-[#00A63E] text-white border-transparent dark:bg-[#00A63E]/30 dark:text-green-100',
|
|
39
|
+
danger: 'bg-[#E7000B] text-white border-transparent dark:bg-[#E7000B]/30 dark:text-red-100',
|
|
40
|
+
warning: 'bg-[#E17100] text-white border-transparent dark:bg-[#E17100]/30 dark:text-orange-100',
|
|
41
|
+
gray: 'bg-[#4A5565] text-white border-transparent dark:bg-[#4A5565]/30 dark:text-gray-100',
|
|
38
42
|
},
|
|
39
43
|
inverted: {
|
|
40
|
-
primary: 'bg-
|
|
41
|
-
success: 'bg-
|
|
42
|
-
danger: 'bg-
|
|
43
|
-
warning: 'bg-
|
|
44
|
-
gray: 'bg-
|
|
45
|
-
orange: 'bg-orange-600/10 text-orange-600'
|
|
44
|
+
primary: 'bg-[#F0F8FF] text-[#068DFF] border-transparent dark:bg-[#068DFF]/10 dark:text-blue-400',
|
|
45
|
+
success: 'bg-[#DCFCE7] text-[#008236] border-transparent dark:bg-[#00A63E]/10 dark:text-green-400',
|
|
46
|
+
danger: 'bg-[#FFE2E2] text-[#C10007] border-transparent dark:bg-[#E7000B]/10 dark:text-red-400',
|
|
47
|
+
warning: 'bg-[#FEF3C6] text-[#9A5800] border-transparent dark:bg-[#E17100]/10 dark:text-orange-400',
|
|
48
|
+
gray: 'bg-[#F3F4F6] text-[#364153] border-transparent dark:bg-[#4A5565]/10 dark:text-gray-300',
|
|
46
49
|
},
|
|
47
50
|
transparent: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
primary: 'bg-white border-[#B3DCFF] text-[#0066BD] dark:bg-transparent dark:border-[#068DFF]/50 dark:text-blue-400',
|
|
52
|
+
success: 'bg-white border-[#7BF1A8] text-[#008236] dark:bg-transparent dark:border-[#00A63E]/50 dark:text-green-400',
|
|
53
|
+
danger: 'bg-white border-[#FFA2A2] text-[#C10007] dark:bg-transparent dark:border-[#E7000B]/50 dark:text-red-400',
|
|
54
|
+
warning: 'bg-white border-[#FFD230] text-[#9A5800] dark:bg-transparent dark:border-[#E17100]/50 dark:text-orange-400',
|
|
55
|
+
gray: 'bg-white border-[#D1D5DC] text-[#364153] dark:bg-transparent dark:border-[#4A5565]/50 dark:text-gray-300',
|
|
56
|
+
},
|
|
57
|
+
subtle: {
|
|
58
|
+
primary: 'bg-[#068DFF]/5 border-[#B3DCFF] text-[#068DFF] dark:bg-blue-500/10 dark:border-[#068DFF]/40 dark:text-blue-400',
|
|
59
|
+
success: 'bg-[#00A63E]/5 border-[#7BF1A8] text-[#00A63E] dark:bg-green-500/10 dark:border-[#00A63E]/40 dark:text-green-400',
|
|
60
|
+
danger: 'bg-[#E7000B]/5 border-[#FFA2A2] text-[#E7000B] dark:bg-red-500/10 dark:border-[#E7000B]/40 dark:text-red-400',
|
|
61
|
+
warning: 'bg-[#E17100]/5 border-[#FFD230] text-[#E17100] dark:bg-yellow-500/10 dark:border-[#E17100]/40 dark:text-orange-400',
|
|
62
|
+
gray: 'bg-[#4A5565]/5 border-[#D1D5DC] text-[#4A5565] dark:bg-gray-500/10 dark:border-[#4A5565]/40 dark:text-gray-300',
|
|
63
|
+
},
|
|
51
64
|
}
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
const pointSwitchValues: ViewType = {
|
|
67
|
+
default: {
|
|
68
|
+
primary: 'bg-white',
|
|
69
|
+
success: 'bg-white',
|
|
70
|
+
danger: 'bg-white',
|
|
71
|
+
warning: 'bg-white',
|
|
72
|
+
gray: 'bg-white',
|
|
73
|
+
},
|
|
74
|
+
inverted: {
|
|
75
|
+
primary: 'bg-[#068DFF] dark:bg-blue-400',
|
|
76
|
+
success: 'bg-[#00A63E] dark:bg-green-400',
|
|
77
|
+
danger: 'bg-[#E7000B] dark:bg-red-400',
|
|
78
|
+
warning: 'bg-[#E17100] dark:bg-orange-400',
|
|
79
|
+
gray: 'bg-[#6A7282] dark:bg-gray-300',
|
|
80
|
+
},
|
|
81
|
+
transparent: {
|
|
82
|
+
primary: 'bg-[#068DFF] dark:bg-blue-400',
|
|
83
|
+
success: 'bg-[#00A63E] dark:bg-green-400',
|
|
84
|
+
danger: 'bg-[#E7000B] dark:bg-red-400',
|
|
85
|
+
warning: 'bg-[#E17100] dark:bg-orange-400',
|
|
86
|
+
gray: 'bg-[#6A7282] dark:bg-gray-300',
|
|
87
|
+
},
|
|
88
|
+
subtle: {
|
|
89
|
+
primary: 'bg-[#068DFF] dark:bg-blue-400',
|
|
90
|
+
success: 'bg-[#00A63E] dark:bg-green-400',
|
|
91
|
+
danger: 'bg-[#E7000B] dark:bg-red-400',
|
|
92
|
+
warning: 'bg-[#E17100] dark:bg-orange-400',
|
|
93
|
+
gray: 'bg-[#6A7282] dark:bg-gray-300',
|
|
94
|
+
},
|
|
58
95
|
}
|
|
59
96
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
97
|
+
const classes = computed(() =>
|
|
98
|
+
[
|
|
99
|
+
'text-body-md rounded-2xl select-none inline-flex gap-[5.5px] justify-center items-center border',
|
|
100
|
+
typeSwitchValues[props.view][props.type],
|
|
101
|
+
props.size === 'xs' ? 'px-2 py-[2px] text-body-sm font-bold' : '',
|
|
102
|
+
props.size === 'sm' ? 'px-[10px] py-1 text-body-sm font-semibold h-6' : '',
|
|
103
|
+
props.size === 'md' ? 'px-3 py-[6x] text-body-sm font-semibold h-7' : '',
|
|
104
|
+
props.size === 'lg' ? 'px-4 py-[6px] font-semibold' : '',
|
|
105
|
+
])
|
|
67
106
|
|
|
107
|
+
const pointClasses = computed(() =>
|
|
108
|
+
[
|
|
109
|
+
'w-1.5 h-1.5 rounded-full shrink-0',
|
|
110
|
+
pointSwitchValues[props.view][props.type],
|
|
111
|
+
])
|
|
68
112
|
</script>
|
|
113
|
+
|
|
69
114
|
<template>
|
|
70
115
|
<div :class="[[classes], customClasses]">
|
|
71
|
-
<slot name="point"
|
|
116
|
+
<slot name="point">
|
|
117
|
+
<span v-if="point" :class="pointClasses" />
|
|
118
|
+
</slot>
|
|
72
119
|
<slot />
|
|
73
120
|
</div>
|
|
74
121
|
</template>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Component } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Step {
|
|
5
|
+
title: string
|
|
6
|
+
description: string
|
|
7
|
+
icon?: Component
|
|
8
|
+
iconBg?: string
|
|
9
|
+
iconColor?: string
|
|
10
|
+
chips?: string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<{
|
|
14
|
+
steps: Step[]
|
|
15
|
+
interval?: number
|
|
16
|
+
autoplay?: boolean
|
|
17
|
+
pauseOnHover?: boolean
|
|
18
|
+
modelValue?: number
|
|
19
|
+
colsClass?: string
|
|
20
|
+
expandable?: boolean
|
|
21
|
+
}>(), {
|
|
22
|
+
interval: 5000,
|
|
23
|
+
autoplay: true,
|
|
24
|
+
pauseOnHover: true,
|
|
25
|
+
modelValue: undefined,
|
|
26
|
+
colsClass: 'lg:grid-cols-2',
|
|
27
|
+
expandable: true,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const emit = defineEmits<{
|
|
31
|
+
(e: 'update:modelValue', value: number): void
|
|
32
|
+
(e: 'change', value: number): void
|
|
33
|
+
}>()
|
|
34
|
+
|
|
35
|
+
const TICK = 50
|
|
36
|
+
|
|
37
|
+
const internal = ref(props.modelValue ?? 0)
|
|
38
|
+
|
|
39
|
+
const active = computed<number>({
|
|
40
|
+
get: () => props.modelValue ?? internal.value,
|
|
41
|
+
set: (value) => {
|
|
42
|
+
internal.value = value
|
|
43
|
+
emit('update:modelValue', value)
|
|
44
|
+
emit('change', value)
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const elapsed = ref(0)
|
|
49
|
+
let ticker: ReturnType<typeof setInterval> | null = null
|
|
50
|
+
let lastTs = 0
|
|
51
|
+
|
|
52
|
+
const progress = computed(() => {
|
|
53
|
+
if (!props.autoplay || props.steps.length <= 1)
|
|
54
|
+
return 1
|
|
55
|
+
return Math.min(1, elapsed.value / props.interval)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const fillWidth = computed(() => `${progress.value * 100}%`)
|
|
59
|
+
|
|
60
|
+
function stopTicker() {
|
|
61
|
+
if (ticker) {
|
|
62
|
+
clearInterval(ticker)
|
|
63
|
+
ticker = null
|
|
64
|
+
}
|
|
65
|
+
lastTs = 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function startTicker() {
|
|
69
|
+
stopTicker()
|
|
70
|
+
if (!props.autoplay || props.steps.length <= 1)
|
|
71
|
+
return
|
|
72
|
+
ticker = setInterval(() => {
|
|
73
|
+
const now = Date.now()
|
|
74
|
+
const dt = lastTs ? now - lastTs : TICK
|
|
75
|
+
lastTs = now
|
|
76
|
+
elapsed.value += dt
|
|
77
|
+
if (elapsed.value >= props.interval) {
|
|
78
|
+
elapsed.value = 0
|
|
79
|
+
active.value = (active.value + 1) % props.steps.length
|
|
80
|
+
}
|
|
81
|
+
}, TICK)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function goTo(index: number) {
|
|
85
|
+
active.value = index
|
|
86
|
+
elapsed.value = 0
|
|
87
|
+
startTicker()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function onMouseEnter() {
|
|
91
|
+
if (props.pauseOnHover)
|
|
92
|
+
stopTicker()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function onMouseLeave() {
|
|
96
|
+
if (props.pauseOnHover)
|
|
97
|
+
startTicker()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onMounted(startTicker)
|
|
101
|
+
onBeforeUnmount(stopTicker)
|
|
102
|
+
watch(() => [props.interval, props.autoplay, props.steps.length], () => {
|
|
103
|
+
elapsed.value = 0
|
|
104
|
+
startTicker()
|
|
105
|
+
})
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<template>
|
|
109
|
+
<div
|
|
110
|
+
class="grid grid-cols-1 gap-4 lg:gap-10"
|
|
111
|
+
:class="colsClass"
|
|
112
|
+
>
|
|
113
|
+
<ol class="flex flex-col gap-3">
|
|
114
|
+
<li
|
|
115
|
+
v-for="(step, i) in steps"
|
|
116
|
+
:key="i"
|
|
117
|
+
class="ss-card group relative cursor-pointer overflow-hidden rounded-2xl border bg-white transition-all duration-300"
|
|
118
|
+
:class="i === active
|
|
119
|
+
? 'ss-card-active border-blue-200'
|
|
120
|
+
: 'border-gray-200 hover:border-blue-100'"
|
|
121
|
+
@click="goTo(i)"
|
|
122
|
+
>
|
|
123
|
+
<div class="p-4">
|
|
124
|
+
<div
|
|
125
|
+
class="flex gap-4"
|
|
126
|
+
:class="expandable ? 'items-center' : 'items-start'"
|
|
127
|
+
>
|
|
128
|
+
<span
|
|
129
|
+
class="flex size-11 shrink-0 items-center justify-center rounded-xl"
|
|
130
|
+
:class="step.iconBg || 'bg-blue-50'"
|
|
131
|
+
>
|
|
132
|
+
<component
|
|
133
|
+
:is="step.icon"
|
|
134
|
+
v-if="step.icon"
|
|
135
|
+
class="size-5"
|
|
136
|
+
:class="step.iconColor || 'text-blue-600'"
|
|
137
|
+
/>
|
|
138
|
+
</span>
|
|
139
|
+
<div class="min-w-0 flex-1">
|
|
140
|
+
<p class="text-body-md-strong md:text-body-lg-strong text-gray-900">
|
|
141
|
+
{{ step.title }}
|
|
142
|
+
</p>
|
|
143
|
+
|
|
144
|
+
<div v-if="!expandable">
|
|
145
|
+
<p class="mt-1 text-body-sm text-gray-500">
|
|
146
|
+
{{ step.description }}
|
|
147
|
+
</p>
|
|
148
|
+
<div
|
|
149
|
+
v-if="step.chips?.length"
|
|
150
|
+
class="mt-3 flex flex-wrap gap-2"
|
|
151
|
+
>
|
|
152
|
+
<a-status-badge
|
|
153
|
+
v-for="(chip, ci) in step.chips"
|
|
154
|
+
:key="ci"
|
|
155
|
+
type="primary"
|
|
156
|
+
view="inverted"
|
|
157
|
+
size="xs"
|
|
158
|
+
custom-classes="!text-body-sm-medium"
|
|
159
|
+
>
|
|
160
|
+
{{ chip }}
|
|
161
|
+
</a-status-badge>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div
|
|
168
|
+
v-if="expandable"
|
|
169
|
+
class="grid transition-all duration-500 ease-out"
|
|
170
|
+
:class="i === active
|
|
171
|
+
? 'mt-3 grid-rows-[1fr] opacity-100'
|
|
172
|
+
: 'grid-rows-[0fr] opacity-0'"
|
|
173
|
+
>
|
|
174
|
+
<div class="overflow-hidden">
|
|
175
|
+
<p class="text-body-sm text-gray-500">
|
|
176
|
+
{{ step.description }}
|
|
177
|
+
</p>
|
|
178
|
+
<div
|
|
179
|
+
v-if="step.chips?.length"
|
|
180
|
+
class="mt-3 flex flex-wrap gap-2"
|
|
181
|
+
>
|
|
182
|
+
<a-status-badge
|
|
183
|
+
v-for="(chip, ci) in step.chips"
|
|
184
|
+
:key="ci"
|
|
185
|
+
type="primary"
|
|
186
|
+
view="inverted"
|
|
187
|
+
size="xs"
|
|
188
|
+
custom-classes="!text-body-sm-medium"
|
|
189
|
+
>
|
|
190
|
+
{{ chip }}
|
|
191
|
+
</a-status-badge>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<span
|
|
198
|
+
v-if="i === active && autoplay && steps.length > 1"
|
|
199
|
+
class="absolute inset-x-0 bottom-0 h-1 bg-gray-100"
|
|
200
|
+
>
|
|
201
|
+
<span
|
|
202
|
+
class="block h-full rounded-r-full bg-gradient-to-r from-blue-600 to-blue-400 transition-[width] duration-100 ease-linear"
|
|
203
|
+
:style="{ width: fillWidth }"
|
|
204
|
+
/>
|
|
205
|
+
</span>
|
|
206
|
+
</li>
|
|
207
|
+
</ol>
|
|
208
|
+
|
|
209
|
+
<div
|
|
210
|
+
@mouseenter="onMouseEnter"
|
|
211
|
+
@mouseleave="onMouseLeave"
|
|
212
|
+
>
|
|
213
|
+
<div class="flex justify-center">
|
|
214
|
+
<slot
|
|
215
|
+
name="panel"
|
|
216
|
+
:active="active"
|
|
217
|
+
:step="steps[active]"
|
|
218
|
+
:progress="progress"
|
|
219
|
+
:count="steps.length"
|
|
220
|
+
:fill-width="fillWidth"
|
|
221
|
+
:go-to="goTo"
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</template>
|
|
227
|
+
|
|
228
|
+
<style scoped>
|
|
229
|
+
.ss-card-active {
|
|
230
|
+
box-shadow: 0 22px 60px -18px rgb(6 141 255 / 22%);
|
|
231
|
+
}
|
|
232
|
+
</style>
|