@imaginario27/air-ui-ds 1.0.14 → 1.0.16

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.
@@ -47,11 +47,11 @@
47
47
  v-if="icon && !svgIcon"
48
48
  :icon
49
49
  preserveAspectRatio="xMidYMid meet"
50
- :class="iconSizeClass"
50
+ :class="[iconSizeClass, iconClass ]"
51
51
  />
52
52
  <span
53
53
  v-else-if="svgIcon"
54
- :class="iconSizeClass"
54
+ :class="[iconSizeClass, iconClass ]"
55
55
  >
56
56
  <SVGImage
57
57
  :src="svgIcon"
@@ -70,11 +70,11 @@
70
70
  v-if="icon"
71
71
  :icon="icon"
72
72
  preserveAspectRatio="xMidYMid meet"
73
- :class="iconSizeClass"
73
+ :class="[iconSizeClass, iconClass]"
74
74
  />
75
75
  <span
76
76
  v-else-if="svgIcon"
77
- :class="iconSizeClass"
77
+ :class="[iconSizeClass, iconClass]"
78
78
  >
79
79
  <SVGImage
80
80
  :src="svgIcon"
@@ -117,6 +117,7 @@ const props = defineProps({
117
117
  type: String as PropType<string>,
118
118
  default: 'Button text'
119
119
  },
120
+ textClass: String as PropType<string>,
120
121
  size: {
121
122
  type: String as PropType<ButtonSize>,
122
123
  default: ButtonSize.LG,
@@ -136,6 +137,7 @@ const props = defineProps({
136
137
  type: Boolean as PropType<boolean>,
137
138
  default: false,
138
139
  },
140
+ iconClass: String as PropType<string>,
139
141
  disabled: {
140
142
  type: Boolean as PropType<boolean>,
141
143
  default: false,
@@ -169,7 +171,6 @@ const props = defineProps({
169
171
  default: 'Processing...',
170
172
  },
171
173
  id: String as PropType<string>,
172
- textClass: String as PropType<string>,
173
174
  })
174
175
 
175
176
  // Emits
@@ -22,11 +22,11 @@
22
22
  v-if="icon && !svgIcon"
23
23
  :icon
24
24
  preserveAspectRatio="xMidYMid meet"
25
- :class="iconSizeClass"
25
+ :class="[iconSizeClass, iconClass]"
26
26
  />
27
27
  <span
28
28
  v-else-if="svgIcon"
29
- :class="iconSizeClass"
29
+ :class="[iconSizeClass, iconClass]"
30
30
  >
31
31
  <SVGImage
32
32
  :src="svgIcon"
@@ -77,6 +77,7 @@ const props = defineProps({
77
77
  type: Boolean as PropType<boolean>,
78
78
  default: false
79
79
  },
80
+ iconClass: String as PropType<string>,
80
81
  disabled: {
81
82
  type: Boolean as PropType<boolean>,
82
83
  default: false,
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <ActionIconButton
3
+ v-if="buttonType === ButtonType.ACTION_ICON_BUTTON"
4
+ :styleType
5
+ :size
6
+ :icon="currentCopyButtonIcon"
7
+ :iconClass="currentIconClass"
8
+ :disabled
9
+ @click="handleCopy"
10
+ />
11
+ <ActionButton
12
+ v-else
13
+ :styleType
14
+ :size
15
+ :text="currentCopyButtonText"
16
+ :icon="currentCopyButtonIcon"
17
+ :iconPosition
18
+ :iconClass="currentIconClass"
19
+ :disabled
20
+ @click="handleCopy"
21
+ />
22
+ </template>
23
+ <script setup lang="ts">
24
+ // Props
25
+ const props = defineProps({
26
+ textToCopy: {
27
+ type: String as PropType<string>,
28
+ required: true,
29
+ },
30
+ copySuccessText: {
31
+ type: String as PropType<string>,
32
+ default: 'Copied to clipboard!',
33
+ },
34
+ copyErrorText: {
35
+ type: String as PropType<string>,
36
+ default: 'Failed to copy to clipboard.',
37
+ },
38
+ showToast: {
39
+ type: Boolean as PropType<boolean>,
40
+ default: true,
41
+ },
42
+ resetAfter: {
43
+ type: Number as PropType<number>,
44
+ default: 1500,
45
+ },
46
+ buttonType: {
47
+ type: String as PropType<ButtonType>,
48
+ default: ButtonType.ACTION_ICON_BUTTON,
49
+ validator: (value: ButtonType) => Object.values(ButtonType).includes(value),
50
+ },
51
+ styleType: {
52
+ type: String as PropType<
53
+ ButtonStyleType.NEUTRAL_FILLED
54
+ | ButtonStyleType.NEUTRAL_OUTLINED
55
+ | ButtonStyleType.NEUTRAL_TRANSPARENT
56
+ | ButtonStyleType.NEUTRAL_TRANSPARENT_SUBTLE
57
+ | ButtonStyleType.PRIMARY_BRAND_FILLED
58
+ | ButtonStyleType.PRIMARY_BRAND_SOFT
59
+ | ButtonStyleType.PRIMARY_BRAND_TRANSPARENT
60
+ | ButtonStyleType.SECONDARY_BRAND_FILLED
61
+ >,
62
+ default: ButtonStyleType.NEUTRAL_OUTLINED,
63
+ validator: (value: unknown) =>
64
+ typeof value === 'string' &&
65
+ [
66
+ ButtonStyleType.NEUTRAL_FILLED,
67
+ ButtonStyleType.NEUTRAL_OUTLINED,
68
+ ButtonStyleType.NEUTRAL_TRANSPARENT,
69
+ ButtonStyleType.NEUTRAL_TRANSPARENT_SUBTLE,
70
+ ButtonStyleType.PRIMARY_BRAND_FILLED,
71
+ ButtonStyleType.PRIMARY_BRAND_SOFT,
72
+ ButtonStyleType.PRIMARY_BRAND_TRANSPARENT,
73
+ ButtonStyleType.SECONDARY_BRAND_FILLED,
74
+ ].includes(value as ButtonStyleType),
75
+ },
76
+ text: {
77
+ type: String as PropType<string>,
78
+ default: 'Copy'
79
+ },
80
+ size: {
81
+ type: String as PropType<ButtonSize>,
82
+ default: ButtonSize.SM,
83
+ validator: (value: ButtonSize) => Object.values(ButtonSize).includes(value),
84
+ },
85
+ icon: {
86
+ type: String as PropType<any>,
87
+ default: "mdiContentCopy",
88
+ },
89
+ iconPosition: {
90
+ type: String as PropType<IconPosition>,
91
+ default: IconPosition.RIGHT,
92
+ validator: (value: IconPosition) => Object.values(IconPosition).includes(value),
93
+ },
94
+ disabled: {
95
+ type: Boolean as PropType<boolean>,
96
+ default: false,
97
+ },
98
+ })
99
+
100
+ // States
101
+ const currentCopyButtonIcon = ref<any>(props.icon)
102
+ const currentCopyButtonText = ref<string>(props.text)
103
+ const currentIconClass = ref<string | undefined>(undefined)
104
+
105
+ // Emits
106
+ const emit = defineEmits(['success', 'error'])
107
+
108
+ // Initialize toast
109
+ const { $toast } = useNuxtApp()
110
+
111
+ // Methods
112
+ const handleCopy = useThrottleFn(
113
+ async () => {
114
+ const success = await copyToClipboard(props.textToCopy)
115
+
116
+ if (success) {
117
+ currentCopyButtonIcon.value = 'mdiCheck'
118
+ currentCopyButtonText.value = props.copySuccessText
119
+
120
+ if (
121
+ props.styleType === ButtonStyleType.NEUTRAL_OUTLINED ||
122
+ props.styleType === ButtonStyleType.NEUTRAL_TRANSPARENT ||
123
+ props.styleType === ButtonStyleType.NEUTRAL_TRANSPARENT_SUBTLE
124
+ ) {
125
+ currentIconClass.value = 'text-icon-success'
126
+ }
127
+
128
+ if (props.showToast) {
129
+ $toast.success(props.copySuccessText, {
130
+ toastId: 'copy-button-success',
131
+ })
132
+ }
133
+
134
+ emit('success')
135
+ } else {
136
+ currentCopyButtonIcon.value = 'mdiAlertCircleOutline'
137
+ currentCopyButtonText.value = props.copyErrorText
138
+
139
+ if (props.showToast) {
140
+ $toast.error(props.copyErrorText, {
141
+ toastId: 'copy-button-error',
142
+ })
143
+ }
144
+
145
+ emit('error')
146
+ }
147
+
148
+ // Reset UI after delay
149
+ setTimeout(() => {
150
+ currentCopyButtonIcon.value = props.icon
151
+ currentCopyButtonText.value = props.text
152
+ currentIconClass.value = undefined
153
+ }, props.resetAfter)
154
+ },
155
+ props.resetAfter,
156
+ true, // trailing only
157
+ )
158
+ </script>
@@ -4,7 +4,7 @@
4
4
  'flex',
5
5
  'flex-col',
6
6
  'gap-4',
7
- dividedRows && '!gap-0 divide-y divide-border-neutral-subtle border-b border-border-neutral-subtle'
7
+ 'mt-2',
8
8
  ]"
9
9
  >
10
10
  <h3
@@ -12,7 +12,6 @@
12
12
  'form-field-group-title',
13
13
  'font-semibold',
14
14
  'text-text-neutral-subtle',
15
- dividedRows && 'mb-4'
16
15
  ]"
17
16
  >
18
17
  {{ title }}
@@ -27,9 +26,5 @@ defineProps({
27
26
  type: String as PropType<string>,
28
27
  default: 'Group title',
29
28
  },
30
- dividedRows: {
31
- type: Boolean as PropType<boolean>,
32
- default: false,
33
- },
34
29
  })
35
30
  </script>
@@ -4,7 +4,7 @@
4
4
  'flex',
5
5
  'flex-col',
6
6
  'gap-4',
7
- dividedRows && '!gap-0 divide-y divide-border-neutral-subtle border-b border-border-neutral-subtle'
7
+ 'mt-2',
8
8
  ]"
9
9
  >
10
10
  <h3
@@ -12,7 +12,6 @@
12
12
  'form-field-group-title',
13
13
  'font-semibold',
14
14
  'text-text-neutral-subtle',
15
- dividedRows && 'mb-4'
16
15
  ]"
17
16
  >
18
17
  {{ title }}
@@ -27,9 +26,5 @@ defineProps({
27
26
  type: String as PropType<string>,
28
27
  default: 'Group title',
29
28
  },
30
- dividedRows: {
31
- type: Boolean as PropType<boolean>,
32
- default: false,
33
- },
34
29
  })
35
30
  </script>
@@ -123,7 +123,7 @@ const props = defineProps({
123
123
  default: false,
124
124
  },
125
125
  autocomplete: {
126
- type: String as PropType<'on' | 'off'>,
126
+ type: String as PropType<string>,
127
127
  default: 'off',
128
128
  },
129
129
  autofocus: {
@@ -49,15 +49,15 @@ defineProps({
49
49
  label: String as PropType<string>,
50
50
  helpText: String as PropType<string>,
51
51
  buttons: Array as PropType<ToggleButton[]>,
52
- disabled: {
53
- type: Boolean as PropType<boolean>,
54
- default: false,
55
- },
56
52
  modelValue: {
57
53
  type: String as PropType<string>,
58
54
  required: true,
59
55
  },
60
56
  groupStyle: String as PropType<ToggleButtonGroupStyle>,
57
+ disabled: {
58
+ type: Boolean as PropType<boolean>,
59
+ default: false,
60
+ },
61
61
  })
62
62
 
63
63
  // Emits
@@ -119,19 +119,19 @@ const props = defineProps({
119
119
  type: String as PropType<string>,
120
120
  required: true,
121
121
  },
122
+ label: String as PropType<string>,
123
+ helpText: String as PropType<string>,
122
124
  value: { // Value of the radio button
123
125
  type: [String, Number, Boolean] as PropType<string | number | boolean>,
124
126
  required: true,
125
127
  },
126
- label: String as PropType<string>,
127
- helpText: String as PropType<string>,
128
128
  icon: {
129
129
  type: String as PropType<any>,
130
130
  default: 'mdiHelp',
131
131
  },
132
132
  type: {
133
133
  type: String as PropType<
134
- ColorAccent.INFO | ColorAccent.SUCCESS | ColorAccent.DANGER | ColorAccent.PRIMARY_BRAND | ColorAccent.SECONDARY_BRAND
134
+ ColorAccent.INFO | ColorAccent.WARNING| ColorAccent.SUCCESS | ColorAccent.DANGER | ColorAccent.PRIMARY_BRAND | ColorAccent.SECONDARY_BRAND
135
135
  >,
136
136
  default: ColorAccent.PRIMARY_BRAND,
137
137
  validator: (value: ColorAccent) => [
@@ -72,7 +72,6 @@
72
72
  </template>
73
73
 
74
74
  <script setup lang="ts">
75
-
76
75
  // Props
77
76
  const props = defineProps({
78
77
  label: String,
@@ -144,7 +143,6 @@ const runValidation = () => {
144
143
  emit('update:error', result ?? '')
145
144
  }
146
145
 
147
-
148
146
  // Watchers
149
147
  // Watch for changes in local selectedOption and emit upward
150
148
  watch(
@@ -23,7 +23,7 @@
23
23
  'bg-background-surface/30 dark:bg-background-surface/85',
24
24
  ],
25
25
  hasBorder && 'border-b border-border-default',
26
- innerContainerClass,
26
+ headerClass,
27
27
  ]"
28
28
  >
29
29
  <!-- Logo -->
@@ -154,10 +154,10 @@
154
154
 
155
155
  <script setup lang="ts">
156
156
  // Props
157
- defineProps({
158
- pageTitleType: {
159
- type: String as PropType<PageTitleType>,
160
- default: PageTitleType.SIMPLE,
157
+ const props = defineProps({
158
+ pageTitleFormat: {
159
+ type: String as PropType<PageTitleFormat>,
160
+ default: PageTitleFormat.SIMPLE,
161
161
  },
162
162
  navMenuItems: {
163
163
  type: Array as PropType<MenuItem[]>,
@@ -206,7 +206,7 @@ defineProps({
206
206
  type: String as PropType<string>,
207
207
  default: 'lg:hidden min-w-[280px]'
208
208
  },
209
- innerContainerClass: String as PropType<string>,
209
+ headerClass: String as PropType<string>,
210
210
  })
211
211
 
212
212
  // Composables
@@ -221,6 +221,6 @@ const currentPageTitle = computed<string>(() =>
221
221
 
222
222
  // Dynamically set the page title
223
223
  useHead(() => ({
224
- title: pageTitle(currentPageTitle.value, App.NAME),
224
+ title: pageTitle(currentPageTitle.value, App.NAME, props.pageTitleFormat),
225
225
  }))
226
226
  </script>
@@ -8,7 +8,7 @@
8
8
  hasSidePadding && 'px-content-side-padding-mobile md:px-content-side-padding',
9
9
  'pt-12',
10
10
  'pb-8',
11
- 'border-b border-border-default'
11
+ hasSeparator && 'border-b border-border-default'
12
12
  ]"
13
13
  >
14
14
  <NavLink
@@ -43,7 +43,7 @@
43
43
  </h1>
44
44
 
45
45
  <p
46
- v-if="pageDescription || description"
46
+ v-if="showDescription && (pageDescription || description)"
47
47
  class="text-text-neutral-subtle text-lg max-w-[800px]"
48
48
  >
49
49
  {{ description ? description : pageDescription }}
@@ -60,13 +60,14 @@ defineProps({
60
60
  },
61
61
  title: String as PropType<string>,
62
62
  overtitle: String as PropType<string>,
63
- overtitleClass: String as PropType<string>,
64
- description: String as PropType<string>,
65
63
  isOverTitleUppercase: Boolean as PropType<boolean>,
66
- hasSidePadding: {
64
+ overtitleClass: String as PropType<string>,
65
+ showDescription: {
67
66
  type: Boolean as PropType<boolean>,
68
67
  default: true,
69
68
  },
69
+ description: String as PropType<string>,
70
+
70
71
  hasGoBackLink: {
71
72
  type: Boolean as PropType<boolean>,
72
73
  default: false,
@@ -79,6 +80,14 @@ defineProps({
79
80
  type: String as PropType<string>,
80
81
  default: '',
81
82
  },
83
+ hasSeparator: {
84
+ type: Boolean as PropType<boolean>,
85
+ default: true,
86
+ },
87
+ hasSidePadding: {
88
+ type: Boolean as PropType<boolean>,
89
+ default: true,
90
+ },
82
91
  })
83
92
 
84
93
  // Page title
@@ -1,5 +1,4 @@
1
1
  export const useTableOfContents = () => {
2
- // Tracks the currently active heading
3
2
  const activeId = ref<string | null>(null)
4
3
  const route = useRoute()
5
4
 
@@ -26,21 +25,37 @@ export const useTableOfContents = () => {
26
25
  const headings = document.querySelectorAll('h2, h3')
27
26
  headings.forEach((heading) => observer!.observe(heading))
28
27
 
29
- // First heading is active by default when the page loads
30
28
  if (headings.length > 0) {
31
29
  const firstHeading = headings[0] as HTMLElement
32
30
  activeId.value = firstHeading.id ?? null
33
31
  }
34
32
  }
35
33
 
34
+ const scrollToHash = () => {
35
+ if (route.hash) {
36
+ const target = document.getElementById(route.hash.slice(1))
37
+ if (target) {
38
+ target.scrollIntoView({ behavior: 'smooth' })
39
+ }
40
+ }
41
+ }
42
+
43
+ const setupObserver = () => {
44
+ nextTick(() => {
45
+ initObserver()
46
+ scrollToHash()
47
+ })
48
+ }
49
+
50
+ onMounted(() => {
51
+ setupObserver()
52
+ })
53
+
36
54
  watch(
37
55
  () => route.path,
38
56
  () => {
39
- nextTick(() => {
40
- initObserver()
41
- })
42
- },
43
- { immediate: true }
57
+ setupObserver()
58
+ }
44
59
  )
45
60
 
46
61
  onBeforeUnmount(() => {
@@ -48,4 +63,4 @@ export const useTableOfContents = () => {
48
63
  })
49
64
 
50
65
  return { activeId }
51
- }
66
+ }
@@ -36,3 +36,8 @@ export enum ButtonPaginationStyle {
36
36
  BUTTON = 'button',
37
37
  OVERLINE = 'overline',
38
38
  }
39
+
40
+ export enum ButtonType {
41
+ ACTION_BUTTON = 'action-button',
42
+ ACTION_ICON_BUTTON = 'action-icon-button',
43
+ }
@@ -0,0 +1,4 @@
1
+ export enum NotificationType {
2
+ INLINE = 'inline',
3
+ TOAST = 'toast',
4
+ }
@@ -1,7 +1,7 @@
1
1
  export enum PageTitleType {
2
2
  SIMPLE = 'simple',
3
- WITH_OVERTITLE = 'withOvertitle',
4
- WITH_BREADCRUMBS = 'withBreadcrumbs',
3
+ WITH_OVERTITLE = 'with-overtitle',
4
+ WITH_BREADCRUMBS = 'with-breadcrumbs',
5
5
  }
6
6
 
7
7
  export enum PageTitleFormat {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imaginario27/air-ui-ds",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "author": "imaginario27",
5
5
  "type": "module",
6
6
  "homepage": "https://air-ui.netlify.app/",