@imaginario27/air-ui-ds 1.0.17 → 1.0.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.
@@ -0,0 +1,292 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ 'w-full',
5
+ isFullScreen ? 'h-screen py-[10vw] md:py-[20vw]' : 'py-6',
6
+ 'px-content-side-padding',
7
+ 'flex',
8
+ orientation === Orientation.VERTICAL ? 'flex-col' : 'flex-col md:flex-row',
9
+ 'gap-9',
10
+ 'items-center',
11
+ 'justify-center',
12
+ ]"
13
+ >
14
+ <!-- Slot for images, illustrations or other visual elements -->
15
+ <slot name="visual-left" />
16
+
17
+ <!-- Body -->
18
+ <div
19
+ :class="[
20
+ 'w-full',
21
+ 'gap-9',
22
+ 'flex',
23
+ 'flex-col',
24
+ contentAlignmentClass,
25
+ ]"
26
+ >
27
+ <slot name="visual-top" />
28
+
29
+ <ContainedIcon
30
+ v-if="!$slots['visual-top'] && showIcon"
31
+ :color="ColorAccent.DANGER"
32
+ :icon="resolvedErrorMapping.icon ?? icon"
33
+ :size="IconContainerSize.XXL"
34
+ />
35
+
36
+ <div
37
+ v-if="!$slots['description']"
38
+ :class="[
39
+ 'flex',
40
+ 'flex-col',
41
+ 'gap-4',
42
+ contentAlignmentClass,
43
+ 'w-full',
44
+ 'max-w-[600px]',
45
+ ]"
46
+ >
47
+ <Heading
48
+ :title="resolvedErrorMapping.title"
49
+ :overtitle="showErrorCode ? statusCode.toString() : ''"
50
+ :align="alignContent"
51
+ :size="HeadingSize.MD"
52
+ :isMobileCentered
53
+ />
54
+
55
+ <p
56
+ :class="[
57
+ 'text-text-neutral-subtle',
58
+ 'font-semibold',
59
+ textAlignmentClass,
60
+ 'leading-6',
61
+ ]"
62
+ >
63
+ {{ resolvedErrorMapping.message }}
64
+ </p>
65
+ </div>
66
+
67
+ <slot name="description" />
68
+
69
+ <!-- Actions -->
70
+ <div
71
+ :class="[
72
+ 'w-full',
73
+ 'flex',
74
+ 'gap-3',
75
+ actionsAlignmentClass,
76
+ 'flex-col',
77
+ 'md:flex-row',
78
+ 'mb-4 md:mb-10', // Visual fix to push content a bit up
79
+ ]"
80
+ >
81
+ <ActionButton
82
+ v-if="!$slots['actions']"
83
+ :actionType="ButtonActionType.LINK"
84
+ :styleType="ButtonStyleType.PRIMARY_BRAND_FILLED"
85
+ :text="backToHomeText"
86
+ class="w-full md:w-auto"
87
+ :icon="backToHomeIcon"
88
+ :iconPosition="IconPosition.LEFT"
89
+ :to="homeRoute"
90
+ />
91
+
92
+ <slot name="actions" />
93
+ </div>
94
+ </div>
95
+
96
+ <!-- Slot for images, illustrations or other visual elements -->
97
+ <slot name="visual-right" />
98
+ </div>
99
+ </template>
100
+
101
+ <script setup lang="ts">
102
+ // Props
103
+ const props = defineProps({
104
+ statusCode: {
105
+ type: Number,
106
+ required: true,
107
+ },
108
+ errorMappings: {
109
+ type: Array as PropType<ErrorMapping[]>,
110
+ default: () => [],
111
+ },
112
+ showIcon: {
113
+ type: Boolean as PropType<boolean>,
114
+ default: true,
115
+ },
116
+ showErrorCode: {
117
+ type: Boolean as PropType<boolean>,
118
+ default: true,
119
+ },
120
+ icon: {
121
+ type: String as PropType<any>,
122
+ default: 'mdiAlertCircleOutline',
123
+ },
124
+ backToHomeText: {
125
+ type: String as PropType<string>,
126
+ default: 'Back to home page',
127
+ },
128
+ backToHomeIcon: {
129
+ type: String as PropType<string>,
130
+ default: 'mdiHomeOutline',
131
+ },
132
+ homeRoute: {
133
+ type: String as PropType<string>,
134
+ default: '/',
135
+ },
136
+ isFullScreen: {
137
+ type: Boolean as PropType<boolean>,
138
+ default: true,
139
+ },
140
+ orientation: {
141
+ type: String as PropType<Orientation>,
142
+ default: Orientation.HORIZONTAL,
143
+ validator: (value: Orientation) => Object.values(Orientation).includes(value),
144
+ },
145
+ alignContent: {
146
+ type: String as PropType<Align>,
147
+ default: Align.CENTER,
148
+ validator: (value: Align) => Object.values(Align).includes(value),
149
+ },
150
+ isMobileCentered: {
151
+ type: Boolean as PropType<boolean>,
152
+ default: true,
153
+ },
154
+ })
155
+
156
+ // Constants
157
+ const defaultErrorMappings: ErrorMapping[] = [
158
+ // 4xx Client Errors
159
+ {
160
+ statusCode: 400,
161
+ title: 'Bad request',
162
+ message: 'The request could not be understood by the server due to malformed syntax.',
163
+ },
164
+ {
165
+ statusCode: 401,
166
+ title: 'Unauthorized',
167
+ message: 'You are not authorized to access this resource. Please log in.',
168
+ },
169
+ {
170
+ statusCode: 403,
171
+ title: 'Forbidden',
172
+ message: 'You do not have permission to access this page.',
173
+ },
174
+ {
175
+ statusCode: 404,
176
+ title: 'Page not found',
177
+ message: 'The page you are looking for does not exist or may have been moved.',
178
+ },
179
+ {
180
+ statusCode: 405,
181
+ title: 'Method not allowed',
182
+ message: 'The method is not allowed for the requested URL.',
183
+ },
184
+ {
185
+ statusCode: 408,
186
+ title: 'Request timeout',
187
+ message: 'The server timed out waiting for the request.',
188
+ },
189
+ {
190
+ statusCode: 429,
191
+ title: 'Too many requests',
192
+ message: 'You have made too many requests in a short period of time. Please try again later.',
193
+ },
194
+
195
+ // 5xx Server Errors
196
+ {
197
+ statusCode: 500,
198
+ title: 'Internal server error',
199
+ message: 'Something went wrong on our end. Please try again later.',
200
+ },
201
+ {
202
+ statusCode: 501,
203
+ title: 'Not implemented',
204
+ message: 'The server does not support the functionality required to fulfill the request.',
205
+ },
206
+ {
207
+ statusCode: 502,
208
+ title: 'Bad gateway',
209
+ message: 'The server received an invalid response from the upstream server.',
210
+ },
211
+ {
212
+ statusCode: 503,
213
+ title: 'Service unavailable',
214
+ message: 'The server is temporarily unable to handle the request. Please try again later.',
215
+ },
216
+ {
217
+ statusCode: 504,
218
+ title: 'Gateway timeout',
219
+ message: 'The upstream server failed to send a request in time.',
220
+ },
221
+ {
222
+ statusCode: 505,
223
+ title: 'HTTP version not supported',
224
+ message: 'The server does not support the HTTP protocol version used in the request.',
225
+ },
226
+
227
+ // Fallback for unknown errors
228
+ {
229
+ statusCode: -1,
230
+ title: 'Unexpected error',
231
+ message: 'An unknown error occurred. Please try again.',
232
+ }
233
+ ]
234
+
235
+
236
+ // Computed
237
+ const resolvedErrorMapping = computed(() => {
238
+ const code = Number(props.statusCode)
239
+
240
+ const combined = [...props.errorMappings, ...defaultErrorMappings]
241
+
242
+ return (
243
+ combined.find(mapping => mapping.statusCode === code) ??
244
+ combined.find(mapping => mapping.statusCode === -1)!
245
+ )
246
+ })
247
+ const pageTitleText = computed(() => resolvedErrorMapping.value.title)
248
+
249
+ watchEffect(() => {
250
+ document.title = pageTitle(pageTitleText.value, App.NAME)
251
+ })
252
+
253
+ // Alignment classes
254
+ const contentAlignmentClass = computed(() => {
255
+ const base = {
256
+ [Align.LEFT]: 'md:items-start',
257
+ [Align.CENTER]: 'md:items-center',
258
+ [Align.RIGHT]: 'md:items-end',
259
+ }
260
+
261
+ return [
262
+ props.isMobileCentered ? 'items-center' : '',
263
+ base[props.alignContent as Align] || 'md:items-center',
264
+ ].join(' ').trim()
265
+ })
266
+
267
+ const actionsAlignmentClass = computed(() => {
268
+ const base = {
269
+ [Align.LEFT]: 'md:items-start md:justify-start',
270
+ [Align.CENTER]: 'md:items-center md:justify-center',
271
+ [Align.RIGHT]: 'md:items-end md:justify-end',
272
+ }
273
+
274
+ return [
275
+ props.isMobileCentered ? 'items-center justify-center' : '',
276
+ base[props.alignContent as Align] || 'md:items-center md:justify-center',
277
+ ].join(' ').trim()
278
+ })
279
+
280
+ const textAlignmentClass = computed(() => {
281
+ const base = {
282
+ [Align.LEFT]: 'md:text-left',
283
+ [Align.CENTER]: 'md:text-center',
284
+ [Align.RIGHT]: 'md:text-right',
285
+ }
286
+
287
+ return [
288
+ props.isMobileCentered ? 'text-center' : '',
289
+ base[props.alignContent as Align] || 'md:text-center',
290
+ ].join(' ').trim()
291
+ })
292
+ </script>
@@ -28,7 +28,7 @@
28
28
  {{ credits }}
29
29
  </span>
30
30
 
31
- <nav v-if="menuItems?.length">
31
+ <nav v-if="menuItems.length">
32
32
  <ul class="flex flex-col lg:flex-row gap-5">
33
33
  <li
34
34
  v-for="(item, index) in menuItems"
@@ -48,7 +48,7 @@
48
48
  </ul>
49
49
  </nav>
50
50
 
51
- <div v-if="socialNetworks?.length" class="flex gap-5">
51
+ <div v-if="socialNetworks.length" class="flex gap-5">
52
52
  <a
53
53
  v-for="(network, index) in socialNetworks"
54
54
  :key="index"
@@ -86,8 +86,14 @@ defineProps({
86
86
  type: String as PropType<string>,
87
87
  default: '© <year> <your-company>. All rights reserved.',
88
88
  },
89
- menuItems: Array as PropType<MenuItem[]>,
90
- socialNetworks: Array as PropType<SocialNetwork[]>,
89
+ menuItems: {
90
+ type: Array as PropType<MenuItem[]>,
91
+ default: () => [],
92
+ },
93
+ socialNetworks: {
94
+ type: Array as PropType<SocialNetwork[]>,
95
+ default: () => [],
96
+ },
91
97
  hasContentMaxWidth: {
92
98
  type: Boolean as PropType<boolean>,
93
99
  default: false,
@@ -95,9 +95,9 @@ const props = defineProps({
95
95
  // Computed classes
96
96
  const alignmentClasses = computed(() => {
97
97
  const alignMap = {
98
- [Align.LEFT]: 'lg:items-start lg:text-left',
99
- [Align.CENTER]: 'lg:items-center lg:text-center',
100
- [Align.RIGHT]: 'lg:items-end lg:text-right',
98
+ [Align.LEFT]: 'md:items-start md:text-left',
99
+ [Align.CENTER]: 'md:items-center md:text-center',
100
+ [Align.RIGHT]: 'md:items-end md:text-right',
101
101
  }
102
102
 
103
103
  if (props.isMobileCentered) {
@@ -0,0 +1,6 @@
1
+ export type ErrorMapping = {
2
+ statusCode: number
3
+ title: string
4
+ message: string
5
+ icon?: string
6
+ }
package/package.json CHANGED
@@ -1,57 +1,57 @@
1
- {
2
- "name": "@imaginario27/air-ui-ds",
3
- "version": "1.0.17",
4
- "author": "imaginario27",
5
- "type": "module",
6
- "homepage": "https://air-ui.netlify.app/",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/imaginario27/air-ui.git"
10
- },
11
- "publishConfig": {
12
- "access": "public"
13
- },
14
- "scripts": {
15
- "build": "nuxt build",
16
- "dev": "nuxt dev",
17
- "generate": "nuxt generate",
18
- "preview": "nuxt preview",
19
- "postinstall": "nuxt prepare",
20
- "test": "vitest",
21
- "generate-theme": "ts-node scripts/generate-theme.ts",
22
- "update-theme-colors": "ts-node scripts/update-ui-theme-colors.ts",
23
- "typecheck": "vue-tsc --noEmit -p tsconfig.typecheck.json"
24
- },
25
- "dependencies": {
26
- "@jaxtheprime/vue3-dropzone": "3.4.0",
27
- "@nuxt/content": "3.7.1",
28
- "@nuxt/eslint": "1.9.0",
29
- "@nuxt/image": "1.11.0",
30
- "@nuxtjs/i18n": "10.1.0",
31
- "@tailwindcss/vite": "4.1.13",
32
- "@vueuse/core": "13.9.0",
33
- "@vueuse/nuxt": "13.9.0",
34
- "jspdf": "3.0.3",
35
- "jspdf-autotable": "5.0.2",
36
- "nuxt": "4.1.2",
37
- "nuxt-mdi": "2.1.1",
38
- "qrcode.vue": "3.6.0",
39
- "tailwindcss": "4.1.13",
40
- "vue": "3.5.22",
41
- "vue-json-excel3": "1.0.30",
42
- "vue-router": "4.5.1",
43
- "vue3-toastify": "0.2.8"
44
- },
45
- "devDependencies": {
46
- "@nuxt/test-utils": "3.19.2",
47
- "@vitest/coverage-v8": "3.2.4",
48
- "@vue/test-utils": "2.4.6",
49
- "eslint": "9.36.0",
50
- "happy-dom": "18.0.1",
51
- "playwright-core": "1.55.1",
52
- "prettier": "3.6.2",
53
- "ts-node": "10.9.2",
54
- "typescript": "5.9.3",
55
- "vitest": "3.2.4"
56
- }
57
- }
1
+ {
2
+ "name": "@imaginario27/air-ui-ds",
3
+ "version": "1.0.19",
4
+ "author": "imaginario27",
5
+ "type": "module",
6
+ "homepage": "https://air-ui.netlify.app/",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/imaginario27/air-ui.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "scripts": {
15
+ "build": "nuxt build",
16
+ "dev": "nuxt dev",
17
+ "generate": "nuxt generate",
18
+ "preview": "nuxt preview",
19
+ "postinstall": "nuxt prepare",
20
+ "test": "vitest",
21
+ "generate-theme": "ts-node scripts/generate-theme.ts",
22
+ "update-theme-colors": "ts-node scripts/update-ui-theme-colors.ts",
23
+ "typecheck": "vue-tsc --noEmit -p tsconfig.typecheck.json"
24
+ },
25
+ "dependencies": {
26
+ "@jaxtheprime/vue3-dropzone": "3.4.0",
27
+ "@nuxt/content": "3.7.1",
28
+ "@nuxt/eslint": "1.9.0",
29
+ "@nuxt/image": "1.11.0",
30
+ "@nuxtjs/i18n": "10.1.0",
31
+ "@tailwindcss/vite": "4.1.13",
32
+ "@vueuse/core": "13.9.0",
33
+ "@vueuse/nuxt": "13.9.0",
34
+ "jspdf": "3.0.3",
35
+ "jspdf-autotable": "5.0.2",
36
+ "nuxt": "4.1.2",
37
+ "nuxt-mdi": "2.1.1",
38
+ "qrcode.vue": "3.6.0",
39
+ "tailwindcss": "4.1.13",
40
+ "vue": "3.5.22",
41
+ "vue-json-excel3": "1.0.30",
42
+ "vue-router": "4.5.1",
43
+ "vue3-toastify": "0.2.8"
44
+ },
45
+ "devDependencies": {
46
+ "@nuxt/test-utils": "3.19.2",
47
+ "@vitest/coverage-v8": "3.2.4",
48
+ "@vue/test-utils": "2.4.6",
49
+ "eslint": "9.36.0",
50
+ "happy-dom": "18.0.1",
51
+ "playwright-core": "1.55.1",
52
+ "prettier": "3.6.2",
53
+ "ts-node": "10.9.2",
54
+ "typescript": "5.9.3",
55
+ "vitest": "3.2.4"
56
+ }
57
+ }