@imaginario27/air-ui-ds 1.0.0 → 1.0.2

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,62 @@
1
+ # @imaginario27/air-ui-ds
2
+
3
+ A modular Design System and UI component library built for Vue and Nuxt. 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 and Nuxt 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 latest version integration
14
+ * Auto-import support for components and composables in Nuxt
15
+ * Utilities for i18n, file uploads, images, PDF generation, and QR codes
16
+ * Full testing setup with Vitest, Vue Test Utils, and Nuxt Test Utils
17
+
18
+
19
+ ## Installation
20
+
21
+ Install using npm:
22
+
23
+ ```bash
24
+ npm install @imaginario27/air-ui-ds
25
+ ```
26
+
27
+ Using pnpm:
28
+
29
+ ```bash
30
+ pnpm add @imaginario27/air-ui-ds
31
+ ```
32
+
33
+ Using yarn:
34
+
35
+ ```bash
36
+ yarn add @imaginario27/air-ui-ds
37
+ ```
38
+
39
+ ## Theme Generation
40
+
41
+ You can generate theme tokens and CSS variables using the built-in script:
42
+
43
+ ```bash
44
+ npm run generate-theme
45
+ ```
46
+
47
+ This regenerates the theme files used by Tailwind CSS and the Design System.
48
+
49
+ ## Testing
50
+
51
+ This package includes a complete testing environment using:
52
+
53
+ * Vitest
54
+ * Vue Test Utils
55
+ * Nuxt Test Utils
56
+ * Happy DOM
57
+
58
+ Run the tests with:
59
+
60
+ ```bash
61
+ npm run test
62
+ ```
@@ -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.2",
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",