@imaginario27/air-ui-ds 1.0.0 → 1.0.1

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/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @imaginario27/air-ui-ds
2
+
3
+ A modular Design System and UI component library built for Vue 3 and Nuxt 3. It provides a scalable architecture for building consistent interfaces, theme management, and reusable components powered by Tailwind CSS and TypeScript.
4
+
5
+ Documentation: [https://air-ui.netlify.app/](https://air-ui.netlify.app/)
6
+
7
+ ## Features
8
+
9
+ * Vue 3 and Nuxt 3 compatibility
10
+ * Design System tokens for colors, spacing, typography, and themes
11
+ * Reusable and typed UI components
12
+ * Light and dark theme system with automatic CSS variable generation
13
+ * Tailwind CSS v4 integration
14
+ * Auto-import support for components and composables in Nuxt
15
+ * Utilities for i18n, file uploads, images, PDF generation, and QR codes
16
+ * Documentation support using Nuxt Content
17
+ * Full testing setup with Vitest, Vue Test Utils, and Nuxt Test Utils
18
+
19
+
20
+ ## Installation
21
+
22
+ Install using npm:
23
+
24
+ ```bash
25
+ npm install @imaginario27/air-ui-ds
26
+ ```
27
+
28
+ Using pnpm:
29
+
30
+ ```bash
31
+ pnpm add @imaginario27/air-ui-ds
32
+ ```
33
+
34
+ Using yarn:
35
+
36
+ ```bash
37
+ yarn add @imaginario27/air-ui-ds
38
+ ```
39
+
40
+ ## Theme Generation
41
+
42
+ You can generate theme tokens and CSS variables using the built-in script:
43
+
44
+ ```bash
45
+ npm run generate-theme
46
+ ```
47
+
48
+ This regenerates the theme files used by Tailwind CSS and the Design System.
49
+
50
+ ## Testing
51
+
52
+ This package includes a complete testing environment using:
53
+
54
+ * Vitest
55
+ * Happy DOM
56
+
57
+ Run the tests with:
58
+
59
+ ```bash
60
+ npm run test
61
+ ```
@@ -1,32 +1,60 @@
1
1
  <template>
2
2
  <header
3
3
  :class="[
4
- isSticky && 'sticky top-0 z-50'
4
+ isSticky && 'sticky top-0 z-50',
5
5
  ]"
6
6
  >
7
7
  <slot name="top-header" />
8
+
8
9
  <!-- Main header wrapper -->
9
10
  <div
10
11
  :class="[
12
+ 'w-full',
11
13
  'flex',
12
14
  'items-center',
13
15
  'justify-between',
14
16
  'gap-3',
17
+ 'mx-auto',
15
18
  'px-content-side-padding-mobile md:px-content-side-padding',
16
19
  'py-4',
17
20
  'bg-background-surface',
18
- hasBorder && 'border-b border-border-default',
19
- isSticky && 'sticky top-0 z-50',
20
21
  hasGlassEffect && [
21
22
  'backdrop-blur-md',
22
23
  'bg-background-surface/30 dark:bg-background-surface/85',
23
24
  ],
24
- containerClass,
25
+ hasBorder && 'border-b border-border-default',
26
+ innerContainerClass,
25
27
  ]"
26
28
  >
27
29
  <!-- Logo -->
28
30
  <div class="flex gap-4">
31
+ <template
32
+ v-if="
33
+ showMobileSidebarToggle
34
+ && sidebarTogglePosition === SidebarTogglePosition.LOGO_LEFT_SIDE
35
+ "
36
+ >
37
+ <ActionIconButton
38
+ :icon="isMobileSidebarOpen ? 'mdiMenuOpen' : 'mdiMenuClose'"
39
+ class="lg:hidden shadow-sm"
40
+ @click="toggleMobileSidebar"
41
+ />
42
+ </template>
43
+
29
44
  <slot name="header-logo" />
45
+
46
+ <template
47
+ v-if="
48
+ showMobileSidebarToggle
49
+ && sidebarTogglePosition === SidebarTogglePosition.LOGO_RIGHT_SIDE
50
+ "
51
+ >
52
+ <ActionIconButton
53
+ :icon="isMobileSidebarOpen ? 'mdiMenuOpen' : 'mdiMenuClose'"
54
+ class="lg:hidden shadow-sm"
55
+ @click="toggleMobileSidebar"
56
+ />
57
+ </template>
30
58
  </div>
31
59
 
32
60
  <!-- Navigation -->
@@ -51,6 +79,7 @@
51
79
  <DropdownMenu
52
80
  v-if="userMenuItems.length && userFullname"
53
81
  class="min-w-[200px]"
82
+ :positionYOffset="8"
54
83
  >
55
84
  <template #activator="{ onClick }">
56
85
  <Avatar
@@ -78,8 +107,11 @@
78
107
  </DropdownMenu>
79
108
 
80
109
  <!-- Mobile menu -->
81
- <template v-if="isMobile && mobileMenuType === MobileNavigationMenuType.DROPDOWN">
82
- <DropdownMenu :class="navMobileMenuClass">
110
+ <template v-if="isMobile && showMobileMenuToggle">
111
+ <DropdownMenu
112
+ :class="navMobileMenuClass"
113
+ :positionYOffset="8"
114
+ >
83
115
  <template #activator="{ onClick }">
84
116
  <ActionIconButton
85
117
  icon="mdiMenu"
@@ -101,7 +133,12 @@
101
133
  </DropdownMenu>
102
134
  </template>
103
135
 
104
- <template v-else-if="mobileMenuType === MobileNavigationMenuType.SIDEBAR">
136
+ <template
137
+ v-else-if="
138
+ showMobileSidebarToggle
139
+ && sidebarTogglePosition === SidebarTogglePosition.RIGHT_SIDE
140
+ "
141
+ >
105
142
  <ActionIconButton
106
143
  :icon="isMobileSidebarOpen ? 'mdiMenuOpen' : 'mdiMenuClose'"
107
144
  class="lg:hidden shadow-sm"
@@ -132,10 +169,18 @@ defineProps({
132
169
  type: Array as PropType<DropdownMenuItem[]>,
133
170
  default: () => [],
134
171
  },
135
- mobileMenuType: {
136
- type: String as PropType<MobileNavigationMenuType>,
137
- default: MobileNavigationMenuType.DROPDOWN,
138
- validator: (value: MobileNavigationMenuType) => Object.values(MobileNavigationMenuType).includes(value),
172
+ showMobileMenuToggle: {
173
+ type: Boolean as PropType<boolean>,
174
+ default: true,
175
+ },
176
+ showMobileSidebarToggle: {
177
+ type: Boolean as PropType<boolean>,
178
+ default: true,
179
+ },
180
+ sidebarTogglePosition: {
181
+ type: String as PropType<SidebarTogglePosition>,
182
+ default: SidebarTogglePosition.RIGHT_SIDE,
183
+ validator: (value: SidebarTogglePosition) => Object.values(SidebarTogglePosition).includes(value),
139
184
  },
140
185
  hasBorder: {
141
186
  type: Boolean as PropType<boolean>,
@@ -161,7 +206,7 @@ defineProps({
161
206
  type: String as PropType<string>,
162
207
  default: 'lg:hidden min-w-[280px]'
163
208
  },
164
- containerClass: String as PropType<string>,
209
+ innerContainerClass: String as PropType<string>,
165
210
  })
166
211
 
167
212
  // Composables
@@ -54,37 +54,36 @@ const props = defineProps({
54
54
  const spacingClass = computed(() => {
55
55
  const variant = {
56
56
  [SectionSpacing.NONE]: 'py-0',
57
- [SectionSpacing.XS]: 'py-section-xs',
58
- [SectionSpacing.SM]: 'py-section-sm',
59
- [SectionSpacing.MD]: 'py-section-md',
60
- [SectionSpacing.LG]: 'py-section-lg',
61
- [SectionSpacing.XL]: 'py-section-xl',
57
+ [SectionSpacing.XS]: 'py-[2vh] sm:py-section-xs lg:py-section-xs',
58
+ [SectionSpacing.SM]: 'py-[4vh] sm:py-section-sm lg:py-section-sm',
59
+ [SectionSpacing.MD]: 'py-[6vh] sm:py-section-md lg:py-section-md',
60
+ [SectionSpacing.LG]: 'py-[8vh] sm:py-section-lg lg:py-section-lg',
61
+ [SectionSpacing.XL]: 'py-[10vh] sm:py-section-xl lg:py-section-xl',
62
62
  }
63
- return variant[props.spacing as SectionSpacing] || 'py-section-xs'
63
+ return variant[props.spacing as SectionSpacing] || variant[SectionSpacing.XS]
64
64
  })
65
65
 
66
66
  const topSpacingClass = computed(() => {
67
67
  const variant = {
68
- [SectionSpacing.NONE]: 'py-0',
69
- [SectionSpacing.XS]: 'pt-section-xs',
70
- [SectionSpacing.SM]: 'pt-section-sm',
71
- [SectionSpacing.MD]: 'pt-section-md',
72
- [SectionSpacing.LG]: 'pt-section-lg',
73
- [SectionSpacing.XL]: 'pt-section-xl',
68
+ [SectionSpacing.NONE]: 'pt-0',
69
+ [SectionSpacing.XS]: 'pt-[2vh] sm:pt-section-xs lg:pt-section-xs',
70
+ [SectionSpacing.SM]: 'pt-[4vh] sm:pt-section-sm lg:pt-section-sm',
71
+ [SectionSpacing.MD]: 'pt-[6vh] sm:pt-section-md lg:pt-section-md',
72
+ [SectionSpacing.LG]: 'pt-[8vh] sm:pt-section-lg lg:pt-section-lg',
73
+ [SectionSpacing.XL]: 'pt-[10vh] sm:pt-section-xl lg:pt-section-xl',
74
74
  }
75
- return variant[props.topSpacing as SectionSpacing]
75
+ return variant[props.topSpacing as SectionSpacing]
76
76
  })
77
77
 
78
78
  const bottomSpacingClass = computed(() => {
79
79
  const variant = {
80
80
  [SectionSpacing.NONE]: '!pb-0',
81
- [SectionSpacing.XS]: '!pb-section-xs',
82
- [SectionSpacing.SM]: '!pb-section-sm',
83
- [SectionSpacing.MD]: '!pb-section-md',
84
- [SectionSpacing.LG]: 'pb-section-lg',
85
- [SectionSpacing.XL]: 'pb-section-xl',
81
+ [SectionSpacing.XS]: 'pb-[2vh] sm:!pb-section-xs lg:!pb-section-xs',
82
+ [SectionSpacing.SM]: 'pb-[4vh] sm:!pb-section-sm lg:!pb-section-sm',
83
+ [SectionSpacing.MD]: 'pb-[6vh] sm:!pb-section-md lg:!pb-section-md',
84
+ [SectionSpacing.LG]: 'pb-[8vh] sm:pb-section-lg lg:pb-section-lg',
85
+ [SectionSpacing.XL]: 'pb-[10vh] sm:pb-section-xl lg:pb-section-xl',
86
86
  }
87
- return variant[props.bottomSpacing as SectionSpacing]
87
+ return variant[props.bottomSpacing as SectionSpacing]
88
88
  })
89
-
90
89
  </script>
@@ -7,11 +7,6 @@ export enum NavLinkSize {
7
7
  XXL = '2xl',
8
8
  }
9
9
 
10
- export enum MobileNavigationMenuType {
11
- DROPDOWN = 'dropdown',
12
- SIDEBAR = 'sidebar',
13
- }
14
-
15
10
  export enum SidebarNavMenuItemStyleType {
16
11
  SPACED = 'spaced',
17
12
  COMPACT = 'sidebar',
@@ -14,6 +14,12 @@ export enum DropdownPosition {
14
14
  BOTTOM_RIGHT = 'bottom-right',
15
15
  }
16
16
 
17
+ export enum SidebarTogglePosition {
18
+ RIGHT_SIDE = 'right-side',
19
+ LOGO_LEFT_SIDE = 'logo-left-side',
20
+ LOGO_RIGHT_SIDE = 'logo-right-side',
21
+ }
22
+
17
23
  export enum Align {
18
24
  LEFT = 'left',
19
25
  CENTER = 'center',
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@imaginario27/air-ui-ds",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "author": "imaginario27",
5
5
  "type": "module",
6
+ "homepage": "https://air-ui.netlify.app/",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "git+https://github.com/imaginario27/air-ui.git"
@@ -17,10 +18,7 @@
17
18
  "preview": "nuxt preview",
18
19
  "postinstall": "nuxt prepare",
19
20
  "test": "vitest",
20
- "generate-theme": "ts-node scripts/generate-theme.ts",
21
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
22
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
23
- "publish:major": "npm version major --no-git-tag-version && npm publish"
21
+ "generate-theme": "ts-node scripts/generate-theme.ts"
24
22
  },
25
23
  "dependencies": {
26
24
  "@jaxtheprime/vue3-dropzone": "^3.4.0",