adata-ui 2.1.38 → 2.1.40-beta

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.
Files changed (41) hide show
  1. package/.nuxtrc +1 -1
  2. package/.playground/app.config.ts +5 -5
  3. package/.playground/app.vue +102 -0
  4. package/README.md +75 -75
  5. package/components/elements/README.md +1 -1
  6. package/components/elements/button-login/index.vue +6 -10
  7. package/components/elements/tree-select/ATreeSelect.vue +5 -1
  8. package/components/elements/tree-select/components/tree-select-nodes.vue +4 -3
  9. package/components/features/color-mode/AColorMode.client.vue +74 -32
  10. package/components/features/dropdown/ADropdownV2.vue +141 -0
  11. package/components/features/lang-switcher/lang-switcher.vue +120 -40
  12. package/components/features//321/201hange-version/AChangeVersion.vue +1 -1
  13. package/components/forms/README.md +1 -1
  14. package/components/navigation/README.md +1 -1
  15. package/components/navigation/footer/AFooter.vue +1 -1
  16. package/components/navigation/header/AHeader.vue +56 -33
  17. package/components/navigation/header/AlmatyContacts.vue +1 -1
  18. package/components/navigation/header/CardGallery.vue +5 -3
  19. package/components/navigation/header/ContactMenu.vue +26 -92
  20. package/components/navigation/header/HeaderLink.vue +189 -215
  21. package/components/navigation/header/HeaderUsage.vue +125 -0
  22. package/components/navigation/header/NavList.vue +35 -50
  23. package/components/navigation/header/ProductMenu.vue +72 -126
  24. package/components/navigation/header/ProfileMenu.vue +131 -150
  25. package/components/navigation/header/SystemNotification.vue +110 -0
  26. package/components/navigation/mobile-navigation/AMobileNavigation.vue +23 -15
  27. package/components/navigation/pill-tabs/APillTabs.vue +7 -2
  28. package/components/overlays/README.md +1 -1
  29. package/components/overlays/tooltip/ATooltipV2.vue +233 -0
  30. package/components/overlays/tooltip/types.ts +26 -0
  31. package/components/overlays/tooltip/useTooltipTrigger.ts +101 -0
  32. package/composables/useHeaderNavigationLinks.ts +15 -8
  33. package/composables/useUrls.ts +1 -1
  34. package/icons/gauge.vue +17 -0
  35. package/icons/sun.vue +13 -3
  36. package/lang/en.ts +6 -0
  37. package/lang/kk.ts +6 -0
  38. package/lang/ru.ts +6 -0
  39. package/package.json +1 -1
  40. package/shared/constans/pages.ts +1 -1
  41. package/components/navigation/header/TopHeader.vue +0 -196
package/.nuxtrc CHANGED
@@ -1 +1 @@
1
- typescript.includeWorkspace = true
1
+ typescript.includeWorkspace = true
@@ -1,5 +1,5 @@
1
- export default defineAppConfig({
2
- myLayer: {
3
- name: 'My amazing Nuxt layer (overwritten)'
4
- }
5
- })
1
+ export default defineAppConfig({
2
+ myLayer: {
3
+ name: 'My amazing Nuxt layer (overwritten)'
4
+ }
5
+ })
@@ -0,0 +1,102 @@
1
+ <script setup lang="ts">
2
+ const manualOpen = ref(false)
3
+ const placements = ['top', 'top-start', 'bottom', 'bottom-end', 'left', 'right'] as const
4
+ </script>
5
+
6
+ <template>
7
+ <div class="min-h-screen bg-gray-50 p-10 dark:bg-gray-950">
8
+ <div class="mx-auto flex max-w-3xl flex-col gap-10">
9
+ <div class="flex items-center justify-between">
10
+ <h1 class="text-xl font-bold text-deepblue-900 dark:text-gray-100">
11
+ ATooltipV2
12
+ </h1>
13
+ <a-color-mode />
14
+ </div>
15
+
16
+ <!-- Placements + arrow (hover/focus) -->
17
+ <section class="flex flex-wrap gap-6">
18
+ <a-tooltip-v2
19
+ v-for="p in placements"
20
+ :key="p"
21
+ :placement="p"
22
+ arrow
23
+ :open-delay="80"
24
+ >
25
+ <button class="rounded-lg bg-blue-700 px-3 py-2 text-sm text-white dark:bg-blue-500 dark:text-gray-900">
26
+ {{ p }}
27
+ </button>
28
+ <template #content>
29
+ <div class="text-[11px] text-deepblue-900 dark:text-gray-100">
30
+ Placement <b>{{ p }}</b>
31
+ </div>
32
+ </template>
33
+ </a-tooltip-v2>
34
+ </section>
35
+
36
+ <!-- Click trigger, interactive content -->
37
+ <section>
38
+ <a-tooltip-v2
39
+ trigger="click"
40
+ placement="bottom-start"
41
+ arrow
42
+ content-class="w-56"
43
+ >
44
+ <button class="rounded-lg px-3 py-2 text-sm text-deepblue-900 ring-1 ring-gray-200 dark:text-gray-100 dark:ring-gray-700">
45
+ Click me
46
+ </button>
47
+ <template #content="{ close }">
48
+ <div class="flex flex-col gap-2 text-[11px] text-deepblue-900 dark:text-gray-100">
49
+ <p>Interactive — outside-click &amp; Esc close.</p>
50
+ <button
51
+ class="self-start text-blue-700 dark:text-blue-400"
52
+ @click="close"
53
+ >
54
+ Close
55
+ </button>
56
+ </div>
57
+ </template>
58
+ </a-tooltip-v2>
59
+ </section>
60
+
61
+ <!-- Manual via v-model:open -->
62
+ <section class="flex items-center gap-4">
63
+ <button
64
+ class="rounded-lg bg-deepblue-900 px-3 py-2 text-sm text-white dark:bg-gray-100 dark:text-gray-900"
65
+ @click="manualOpen = !manualOpen"
66
+ >
67
+ Toggle manual ({{ manualOpen }})
68
+ </button>
69
+ <a-tooltip-v2
70
+ v-model:open="manualOpen"
71
+ trigger="manual"
72
+ placement="right"
73
+ arrow
74
+ >
75
+ <span class="text-sm text-gray-500 dark:text-gray-400">anchor</span>
76
+ <template #content>
77
+ <div class="text-[11px] text-deepblue-900 dark:text-gray-100">
78
+ Manually controlled
79
+ </div>
80
+ </template>
81
+ </a-tooltip-v2>
82
+ </section>
83
+
84
+ <!-- Teleport out of an overflow-hidden container -->
85
+ <section class="h-24 overflow-hidden rounded-lg p-3 ring-1 ring-gray-200 dark:ring-gray-700">
86
+ <a-tooltip-v2
87
+ placement="top"
88
+ arrow
89
+ >
90
+ <button class="rounded-lg bg-green-500 px-3 py-2 text-sm text-white dark:text-gray-900">
91
+ Inside overflow-hidden (teleported)
92
+ </button>
93
+ <template #content>
94
+ <div class="text-[11px] text-deepblue-900 dark:text-gray-100">
95
+ Not clipped
96
+ </div>
97
+ </template>
98
+ </a-tooltip-v2>
99
+ </section>
100
+ </div>
101
+ </div>
102
+ </template>
package/README.md CHANGED
@@ -1,75 +1,75 @@
1
- # Adata UI with Nuxt 3 using Layers
2
-
3
- Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
4
-
5
- ## Setup
6
-
7
- Make sure to install the dependencies:
8
-
9
- ```bash
10
- # npm
11
- npm install
12
-
13
- # pnpm
14
- pnpm install
15
-
16
- # yarn
17
- yarn install
18
-
19
- # bun
20
- bun install
21
- ```
22
-
23
- ## Development Server
24
-
25
- Start the development server on `https://localhost:3000`:
26
-
27
- ```bash
28
- # npm
29
- npm run dev
30
-
31
- # pnpm
32
- pnpm run dev
33
-
34
- # yarn
35
- yarn dev
36
-
37
- # bun
38
- bun run dev
39
- ```
40
-
41
- ## Production
42
-
43
- Build the application for production:
44
-
45
- ```bash
46
- # npm
47
- npm run build
48
-
49
- # pnpm
50
- pnpm run build
51
-
52
- # yarn
53
- yarn build
54
-
55
- # bun
56
- bun run build
57
- ```
58
-
59
- Locally preview production build:
60
-
61
- ```bash
62
- # npm
63
- npm run preview
64
-
65
- # pnpm
66
- pnpm run preview
67
-
68
- # yarn
69
- yarn preview
70
-
71
- # bun
72
- bun run preview
73
- ```
74
-
75
- Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
1
+ # Adata UI with Nuxt 3 using Layers
2
+
3
+ Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
4
+
5
+ ## Setup
6
+
7
+ Make sure to install the dependencies:
8
+
9
+ ```bash
10
+ # npm
11
+ npm install
12
+
13
+ # pnpm
14
+ pnpm install
15
+
16
+ # yarn
17
+ yarn install
18
+
19
+ # bun
20
+ bun install
21
+ ```
22
+
23
+ ## Development Server
24
+
25
+ Start the development server on `https://localhost:3000`:
26
+
27
+ ```bash
28
+ # npm
29
+ npm run dev
30
+
31
+ # pnpm
32
+ pnpm run dev
33
+
34
+ # yarn
35
+ yarn dev
36
+
37
+ # bun
38
+ bun run dev
39
+ ```
40
+
41
+ ## Production
42
+
43
+ Build the application for production:
44
+
45
+ ```bash
46
+ # npm
47
+ npm run build
48
+
49
+ # pnpm
50
+ pnpm run build
51
+
52
+ # yarn
53
+ yarn build
54
+
55
+ # bun
56
+ bun run build
57
+ ```
58
+
59
+ Locally preview production build:
60
+
61
+ ```bash
62
+ # npm
63
+ npm run preview
64
+
65
+ # pnpm
66
+ pnpm run preview
67
+
68
+ # yarn
69
+ yarn preview
70
+
71
+ # bun
72
+ bun run preview
73
+ ```
74
+
75
+ Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
@@ -1 +1 @@
1
- # button, alerts, dropdown
1
+ # button, alerts, dropdown
@@ -1,22 +1,18 @@
1
1
  <script setup lang="ts">
2
- import IconLogout from '#adata-ui/icons/logout.vue'
2
+ defineEmits(['click'])
3
3
 
4
4
  const { t } = useI18n()
5
-
6
- defineEmits(['click'])
7
5
  </script>
8
6
 
9
7
  <template>
10
- <div
11
- class="hidden w-max cursor-pointer items-center gap-2 rounded-2xl bg-blue-700 px-4 py-[6px] font-semibold dark:bg-blue-500 lg:flex"
8
+ <button
9
+ type="button"
10
+ class="hidden h-8 cursor-pointer items-center rounded-xl bg-blue-700 px-3.5 text-sm font-semibold text-white transition-colors duration-150 hover:bg-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:ring-offset-1 dark:bg-blue-500 dark:text-gray-900 dark:hover:bg-blue-600 dark:focus-visible:ring-offset-gray-900 lg:inline-flex"
12
11
  data-test-id="header-login-button"
13
12
  @click="$emit('click')"
14
13
  >
15
- <icon-logout class="text-white dark:text-gray-900 lg:h-4 lg:w-4" />
16
- <span class="hidden text-sm font-semibold text-white dark:text-gray-900 lg:inline-block min-w-[44px]">
17
- {{ t('header.login') }}
18
- </span>
19
- </div>
14
+ {{ t('header.login') }}
15
+ </button>
20
16
  </template>
21
17
 
22
18
  <style scoped></style>
@@ -81,7 +81,11 @@ const toggleExpand = (node) => {
81
81
  }
82
82
 
83
83
  const toggleCheckbox = (id, checked) => {
84
- setState(id, checked ? 'checked' : 'unchecked')
84
+ if (props.onlyOne && !checked) {
85
+ reset()
86
+ } else {
87
+ setState(id, checked ? 'checked' : 'unchecked')
88
+ }
85
89
  }
86
90
 
87
91
  function openFounded(nodes: TreeNestedNode[]) {
@@ -6,7 +6,7 @@
6
6
  ]"
7
7
  >
8
8
  <div class="flex items-center gap-1 p-2" v-if="!node?.hidden"
9
- :class="{ 'bg-deepblue-50 dark:bg-gray-200/5': disabled }"
9
+ :class="{ 'bg-deepblue-50 dark:bg-gray-200/5': disabled && node.state === 'unchecked' }"
10
10
  >
11
11
  <div class="min-w-4">
12
12
  <a-icon-chevron-down
@@ -21,7 +21,7 @@
21
21
  <input
22
22
  :id="node.id.toString()"
23
23
  type="checkbox"
24
- :disabled="disabled"
24
+ :disabled="disabled && node.state === 'unchecked'"
25
25
  class="hidden"
26
26
  :checked="node.state === 'checked'"
27
27
  @change="handleCheckboxChange"
@@ -41,7 +41,7 @@
41
41
  :name="`${node.id}`"
42
42
  :intermediate="node.state === 'indeterminate'"
43
43
  side="right"
44
- :disabled="disabled"
44
+ :disabled="disabled && node.state === 'unchecked'"
45
45
  always-intermediate
46
46
  @change="handleCheckboxChange"
47
47
  >
@@ -57,6 +57,7 @@
57
57
  :toggle-expand="toggleExpand"
58
58
  :toggle-checkbox="toggleCheckbox"
59
59
  :type="type"
60
+ :disabled="disabled"
60
61
  />
61
62
  </template>
62
63
  </ul>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
- import AToggle from "#adata-ui/components/forms/toggle/AToggle.vue";
3
- import Sun from "#adata-ui/icons/sun.vue";
4
- import Moon from "#adata-ui/icons/moon.vue";
2
+ import Moon from '#adata-ui/icons/moon.vue'
3
+ import Sun from '#adata-ui/icons/sun.vue'
4
+ import { useEventListener } from '@vueuse/core'
5
5
 
6
6
  const colorMode = useColorMode()
7
7
  const setColorMode = useCookie('colorMode')
@@ -10,45 +10,87 @@ if (setColorMode.value) {
10
10
  colorMode.preference = setColorMode.value
11
11
  }
12
12
 
13
- const mode = computed({
14
- get() {
15
- return colorMode.value === 'dark'
16
- },
17
- set() {
18
- const value = colorMode.value === 'dark' ? 'light' : 'dark'
19
- const hostname = location.hostname.split('.').reverse()
20
- const maxAge = 60 * 60 * 24 * 365 // 1 год
13
+ const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365
21
14
 
22
- colorMode.preference = value
23
- document.cookie = `colorMode=${value}; max-age=${maxAge}; domain=.${hostname[1]}.${hostname[0]}; path=/`
24
- }
25
- })
15
+ function cookieDomainAttr(): string {
16
+ const parts = location.hostname.split('.').reverse()
17
+ if (parts.length < 2) return ''
18
+ return `; domain=.${parts[1]}.${parts[0]}`
19
+ }
20
+
21
+ const isDark = computed(() => colorMode.value === 'dark')
26
22
 
27
- onMounted(() => {
28
- window.addEventListener('storage', (event) => {
29
- if (event.key === 'nuxt-color-mode') {
30
- colorMode.preference = event.newValue;
31
- }
32
- });
23
+ function toggle() {
24
+ const value = isDark.value ? 'light' : 'dark'
25
+ colorMode.preference = value
26
+ document.cookie = `colorMode=${value}; max-age=${ONE_YEAR_SECONDS}${cookieDomainAttr()}; path=/`
27
+ }
28
+
29
+ useEventListener(window, 'storage', (event: StorageEvent) => {
30
+ if (event.key === 'nuxt-color-mode' && event.newValue) {
31
+ colorMode.preference = event.newValue
32
+ }
33
33
  })
34
34
  </script>
35
35
 
36
36
  <template>
37
37
  <client-only>
38
- <a-toggle
39
- v-model="mode"
40
- size="2xl"
41
- active-class="dark:bg-gray-800"
42
- :off-icon="Sun"
43
- off-icon-class="w-4 h-4"
44
- on-icon-class="w-4 h-4"
45
- :on-icon="Moon"
46
- active-container-class="dark:bg-black"
38
+ <button
39
+ type="button"
40
+ class="relative inline-flex size-8 items-center justify-center overflow-hidden rounded-xl text-deepblue-900/80 transition-colors duration-150 hover:bg-deepblue-900/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:ring-offset-1 dark:text-gray-200 dark:hover:bg-white/10 dark:focus-visible:ring-offset-gray-900"
41
+ :aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
47
42
  data-test-id="header-switch-theme-toggle"
48
- />
43
+ @click="toggle"
44
+ >
45
+ <transition
46
+ enter-active-class="color-mode__icon-enter-active"
47
+ enter-from-class="color-mode__icon-enter-from"
48
+ enter-to-class="color-mode__icon-enter-to"
49
+ leave-active-class="color-mode__icon-leave-active"
50
+ leave-from-class="color-mode__icon-leave-from"
51
+ leave-to-class="color-mode__icon-leave-to"
52
+ mode="out-in"
53
+ >
54
+ <component
55
+ :is="isDark ? Moon : Sun"
56
+ :key="isDark ? 'moon' : 'sun'"
57
+ class="size-5"
58
+ :font-controlled="false"
59
+ filled
60
+ />
61
+ </transition>
62
+ </button>
49
63
  </client-only>
50
64
  </template>
51
65
 
66
+ <style scoped>
67
+ .color-mode__icon-enter-active,
68
+ .color-mode__icon-leave-active {
69
+ transition:
70
+ opacity 180ms cubic-bezier(0.22, 1, 0.36, 1),
71
+ transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
72
+ }
73
+
74
+ .color-mode__icon-enter-from {
75
+ opacity: 0;
76
+ transform: rotate(-90deg) scale(0.6);
77
+ }
78
+
79
+ .color-mode__icon-leave-to {
80
+ opacity: 0;
81
+ transform: rotate(90deg) scale(0.6);
82
+ }
83
+
84
+ .color-mode__icon-enter-to,
85
+ .color-mode__icon-leave-from {
86
+ opacity: 1;
87
+ transform: rotate(0) scale(1);
88
+ }
52
89
 
53
- <style>
90
+ @media (prefers-reduced-motion: reduce) {
91
+ .color-mode__icon-enter-active,
92
+ .color-mode__icon-leave-active {
93
+ transition: none;
94
+ }
95
+ }
54
96
  </style>
@@ -0,0 +1,141 @@
1
+ <script setup lang="ts">
2
+ import type { Placement } from '@floating-ui/vue'
3
+ import { autoUpdate, flip, offset as offsetMiddleware, shift, useFloating } from '@floating-ui/vue'
4
+ import { onClickOutside, onKeyStroke } from '@vueuse/core'
5
+
6
+ defineOptions({ name: 'ADropdownV2' })
7
+
8
+ const props = withDefaults(defineProps<{
9
+ placement?: Placement
10
+ offset?: number
11
+ }>(), {
12
+ placement: 'bottom-end',
13
+ offset: 8,
14
+ })
15
+
16
+ const wrapper = ref<HTMLElement | null>(null)
17
+ const reference = ref<HTMLElement | null>(null)
18
+ const floating = ref<HTMLElement | null>(null)
19
+ const isOpen = ref(false)
20
+
21
+ const { x, y, strategy, update, placement: resolvedPlacement } = useFloating(reference, floating, {
22
+ placement: computed(() => props.placement),
23
+ strategy: 'absolute',
24
+ middleware: computed(() => [offsetMiddleware(props.offset), flip(), shift({ padding: 8 })]),
25
+ })
26
+
27
+ // Anchor the grow/shrink animation to the trigger so the panel feels attached.
28
+ const transformOrigin = computed(() => {
29
+ const [side, align] = resolvedPlacement.value.split('-')
30
+ const vertical = side === 'top' ? 'bottom' : 'top'
31
+ const horizontal = align === 'start' ? 'left' : align === 'end' ? 'right' : 'center'
32
+ return `${vertical} ${horizontal}`
33
+ })
34
+
35
+ let cleanup: (() => void) | null = null
36
+ watch(isOpen, (open) => {
37
+ if (open && reference.value && floating.value) {
38
+ cleanup = autoUpdate(reference.value, floating.value, update)
39
+ }
40
+ else {
41
+ cleanup?.()
42
+ cleanup = null
43
+ }
44
+ })
45
+ onUnmounted(() => cleanup?.())
46
+
47
+ function toggle() {
48
+ isOpen.value = !isOpen.value
49
+ }
50
+ function close() {
51
+ isOpen.value = false
52
+ }
53
+
54
+ onClickOutside(wrapper, () => {
55
+ if (isOpen.value) close()
56
+ })
57
+ onKeyStroke('Escape', () => {
58
+ if (isOpen.value) close()
59
+ })
60
+ </script>
61
+
62
+ <template>
63
+ <div
64
+ ref="wrapper"
65
+ class="relative"
66
+ >
67
+ <div
68
+ ref="reference"
69
+ class="inline-flex"
70
+ >
71
+ <slot
72
+ :toggle="toggle"
73
+ :is-open="isOpen"
74
+ />
75
+ </div>
76
+
77
+ <transition
78
+ enter-active-class="a-dropdown-v2__enter-active"
79
+ enter-from-class="a-dropdown-v2__enter-from"
80
+ enter-to-class="a-dropdown-v2__enter-to"
81
+ leave-active-class="a-dropdown-v2__leave-active"
82
+ leave-from-class="a-dropdown-v2__leave-from"
83
+ leave-to-class="a-dropdown-v2__leave-to"
84
+ >
85
+ <div
86
+ v-if="isOpen"
87
+ ref="floating"
88
+ class="z-[60] overflow-hidden rounded-xl shadow-lg shadow-gray-900/10 dark:shadow-black/30"
89
+ :style="{
90
+ position: strategy,
91
+ left: x != null ? `${x}px` : '',
92
+ top: y != null ? `${y}px` : '',
93
+ transformOrigin,
94
+ }"
95
+ >
96
+ <slot
97
+ name="content"
98
+ :close="close"
99
+ />
100
+ </div>
101
+ </transition>
102
+ </div>
103
+ </template>
104
+
105
+ <style scoped>
106
+ .a-dropdown-v2__enter-active {
107
+ transition:
108
+ opacity 200ms ease,
109
+ transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
110
+ }
111
+
112
+ .a-dropdown-v2__leave-active {
113
+ transition:
114
+ opacity 140ms ease,
115
+ transform 160ms cubic-bezier(0.4, 0, 1, 1);
116
+ }
117
+
118
+ .a-dropdown-v2__enter-from,
119
+ .a-dropdown-v2__leave-to {
120
+ opacity: 0;
121
+ transform: translateY(-8px) scale(0.96);
122
+ }
123
+
124
+ .a-dropdown-v2__enter-to,
125
+ .a-dropdown-v2__leave-from {
126
+ opacity: 1;
127
+ transform: translateY(0) scale(1);
128
+ }
129
+
130
+ @media (prefers-reduced-motion: reduce) {
131
+ .a-dropdown-v2__enter-active,
132
+ .a-dropdown-v2__leave-active {
133
+ transition: opacity 120ms ease;
134
+ }
135
+
136
+ .a-dropdown-v2__enter-from,
137
+ .a-dropdown-v2__leave-to {
138
+ transform: none;
139
+ }
140
+ }
141
+ </style>