@imaginario27/air-ui-ds 1.0.15 → 1.0.17
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/components/buttons/ActionButton.vue +6 -5
- package/components/buttons/ActionIconButton.vue +3 -2
- package/components/buttons/CopyButton.vue +158 -0
- package/components/forms/DataDetailsFieldGroup.vue +1 -6
- package/components/forms/FormFieldGroup.vue +1 -6
- package/components/layouts/Footer.vue +104 -0
- package/components/layouts/Heading.vue +36 -7
- package/components/layouts/Overtitle.vue +28 -2
- package/components/layouts/headers/CompactHeader.vue +7 -7
- package/components/layouts/headers/{PageHeader.vue → ContentPageHeader.vue} +13 -5
- package/models/enums/buttons.ts +5 -0
- package/models/enums/headings.ts +2 -0
- package/models/enums/notifications.ts +4 -0
- package/models/enums/pages.ts +2 -2
- package/models/types/navigation.ts +4 -3
- package/package.json +1 -1
- package/components/layouts/ContainerWrapper.vue +0 -13
- package/components/layouts/headers/WebAppHeader.vue +0 -54
|
@@ -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
|
-
|
|
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
|
-
|
|
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>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<footer
|
|
3
|
+
:class="[
|
|
4
|
+
'w-full',
|
|
5
|
+
'bg-background-surface',
|
|
6
|
+
'py-section-xs',
|
|
7
|
+
hasSidePadding && 'px-content-side-padding-mobile md:px-content-side-padding',
|
|
8
|
+
]"
|
|
9
|
+
>
|
|
10
|
+
<MaxWidthContainer
|
|
11
|
+
:class="[!hasContentMaxWidth && '!max-w-full']"
|
|
12
|
+
>
|
|
13
|
+
<template v-if="!$slots['default']">
|
|
14
|
+
<div
|
|
15
|
+
:class="[
|
|
16
|
+
'w-full',
|
|
17
|
+
'flex',
|
|
18
|
+
'flex-col',
|
|
19
|
+
isMobileCentered &&'items-center text-center',
|
|
20
|
+
'gap-10',
|
|
21
|
+
'lg:flex-row',
|
|
22
|
+
'lg:items-start',
|
|
23
|
+
'lg:text-left',
|
|
24
|
+
'lg:justify-between',
|
|
25
|
+
]"
|
|
26
|
+
>
|
|
27
|
+
<span class="text-sm text-text-neutral-subtle">
|
|
28
|
+
{{ credits }}
|
|
29
|
+
</span>
|
|
30
|
+
|
|
31
|
+
<nav v-if="menuItems?.length">
|
|
32
|
+
<ul class="flex flex-col lg:flex-row gap-5">
|
|
33
|
+
<li
|
|
34
|
+
v-for="(item, index) in menuItems"
|
|
35
|
+
:key="index"
|
|
36
|
+
>
|
|
37
|
+
<NuxtLink
|
|
38
|
+
:to="item.to"
|
|
39
|
+
:class="[
|
|
40
|
+
'text-sm',
|
|
41
|
+
'hover:text-text-primary-brand-hover',
|
|
42
|
+
'transition-colors',
|
|
43
|
+
]"
|
|
44
|
+
>
|
|
45
|
+
{{ item.text }}
|
|
46
|
+
</NuxtLink>
|
|
47
|
+
</li>
|
|
48
|
+
</ul>
|
|
49
|
+
</nav>
|
|
50
|
+
|
|
51
|
+
<div v-if="socialNetworks?.length" class="flex gap-5">
|
|
52
|
+
<a
|
|
53
|
+
v-for="(network, index) in socialNetworks"
|
|
54
|
+
:key="index"
|
|
55
|
+
:href="network.link"
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noopener noreferrer"
|
|
58
|
+
:class="[
|
|
59
|
+
'text-text-neutral-subtle',
|
|
60
|
+
'hover:text-text-primary-brand-hover',
|
|
61
|
+
'transition-colors',
|
|
62
|
+
]"
|
|
63
|
+
:aria-label="network.name"
|
|
64
|
+
>
|
|
65
|
+
<img
|
|
66
|
+
:src="network.icon"
|
|
67
|
+
:alt="`${network.name} icon`"
|
|
68
|
+
width="20"
|
|
69
|
+
height="20"
|
|
70
|
+
class="min-w-[20px] min-h-[20px]"
|
|
71
|
+
/>
|
|
72
|
+
</a>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
<template v-else>
|
|
77
|
+
<slot />
|
|
78
|
+
</template>
|
|
79
|
+
</MaxWidthContainer>
|
|
80
|
+
</footer>
|
|
81
|
+
</template>
|
|
82
|
+
<script setup lang="ts">
|
|
83
|
+
// Props
|
|
84
|
+
defineProps({
|
|
85
|
+
credits: {
|
|
86
|
+
type: String as PropType<string>,
|
|
87
|
+
default: '© <year> <your-company>. All rights reserved.',
|
|
88
|
+
},
|
|
89
|
+
menuItems: Array as PropType<MenuItem[]>,
|
|
90
|
+
socialNetworks: Array as PropType<SocialNetwork[]>,
|
|
91
|
+
hasContentMaxWidth: {
|
|
92
|
+
type: Boolean as PropType<boolean>,
|
|
93
|
+
default: false,
|
|
94
|
+
},
|
|
95
|
+
hasSidePadding: {
|
|
96
|
+
type: Boolean as PropType<boolean>,
|
|
97
|
+
default: true,
|
|
98
|
+
},
|
|
99
|
+
isMobileCentered: {
|
|
100
|
+
type: Boolean as PropType<boolean>,
|
|
101
|
+
default: false,
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
</script>
|
|
@@ -3,26 +3,32 @@
|
|
|
3
3
|
:class="[
|
|
4
4
|
'w-full flex flex-col',
|
|
5
5
|
alignmentClasses,
|
|
6
|
-
titleClass,
|
|
7
6
|
]"
|
|
8
7
|
>
|
|
9
8
|
<!-- Overtitle -->
|
|
10
|
-
<
|
|
9
|
+
<span
|
|
11
10
|
v-if="overtitle"
|
|
12
11
|
:class="[
|
|
13
12
|
'font-semibold',
|
|
14
13
|
overtitleSizeClass,
|
|
15
14
|
'text-text-secondary-brand-default',
|
|
16
15
|
spaceOvertitleClass,
|
|
16
|
+
isOverTitleUppercase && 'uppercase',
|
|
17
|
+
overtitleClass,
|
|
17
18
|
]"
|
|
18
19
|
>
|
|
19
20
|
{{ overtitle }}
|
|
20
|
-
</
|
|
21
|
+
</span>
|
|
21
22
|
|
|
22
23
|
<!-- Dynamic title -->
|
|
23
24
|
<component
|
|
24
25
|
:is="headingTag"
|
|
25
|
-
:class="[
|
|
26
|
+
:class="[
|
|
27
|
+
titleSizeClass,
|
|
28
|
+
'font-semibold',
|
|
29
|
+
'text-text-default',
|
|
30
|
+
titleClass,
|
|
31
|
+
]"
|
|
26
32
|
>
|
|
27
33
|
{{ title }}
|
|
28
34
|
</component>
|
|
@@ -30,7 +36,12 @@
|
|
|
30
36
|
<!-- Description -->
|
|
31
37
|
<p
|
|
32
38
|
v-if="description"
|
|
33
|
-
:class="[
|
|
39
|
+
:class="[
|
|
40
|
+
descriptionSizeClass,
|
|
41
|
+
'text-text-neutral-subtle',
|
|
42
|
+
spaceDescriptionClass,
|
|
43
|
+
descriptionClass,
|
|
44
|
+
]"
|
|
34
45
|
>
|
|
35
46
|
{{ description }}
|
|
36
47
|
</p>
|
|
@@ -41,6 +52,10 @@
|
|
|
41
52
|
// Props
|
|
42
53
|
const props = defineProps({
|
|
43
54
|
overtitle: String as PropType<string>,
|
|
55
|
+
isOverTitleUppercase: {
|
|
56
|
+
type: Boolean as PropType<boolean>,
|
|
57
|
+
default: false,
|
|
58
|
+
},
|
|
44
59
|
title: {
|
|
45
60
|
type: String as PropType<string>,
|
|
46
61
|
default: 'Heading title'
|
|
@@ -64,15 +79,17 @@ const props = defineProps({
|
|
|
64
79
|
Object.values(HeadingSpacing).includes(value as HeadingSpacing),
|
|
65
80
|
},
|
|
66
81
|
headingTag: {
|
|
67
|
-
type: [String, Number] as PropType<'h1' | 'h2' | 'h3'>,
|
|
82
|
+
type: [String, Number] as PropType<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'>,
|
|
68
83
|
default: 'h1',
|
|
69
|
-
validator: (value: string | number) => ['h1', 'h2', 'h3'].includes(value as string)
|
|
84
|
+
validator: (value: string | number) => ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(value as string)
|
|
70
85
|
},
|
|
71
86
|
isMobileCentered: {
|
|
72
87
|
type: Boolean as PropType<boolean>,
|
|
73
88
|
default: false,
|
|
74
89
|
},
|
|
90
|
+
overtitleClass: String as PropType<string>,
|
|
75
91
|
titleClass: String as PropType<string>,
|
|
92
|
+
descriptionClass: String as PropType<string>,
|
|
76
93
|
})
|
|
77
94
|
|
|
78
95
|
// Computed classes
|
|
@@ -100,6 +117,8 @@ const alignmentClasses = computed(() => {
|
|
|
100
117
|
|
|
101
118
|
const titleSizeClass = computed(() => {
|
|
102
119
|
const map = {
|
|
120
|
+
[HeadingSize.XXS]: 'text-lg',
|
|
121
|
+
[HeadingSize.XS]: 'text-xl',
|
|
103
122
|
[HeadingSize.SM]: 'text-2xl',
|
|
104
123
|
[HeadingSize.MD]: 'text-3xl md:text-4xl',
|
|
105
124
|
[HeadingSize.LG]: 'text-4xl md:text-5xl',
|
|
@@ -110,6 +129,8 @@ const titleSizeClass = computed(() => {
|
|
|
110
129
|
|
|
111
130
|
const overtitleSizeClass = computed(() => {
|
|
112
131
|
const map = {
|
|
132
|
+
[HeadingSize.XXS]: 'text-xs',
|
|
133
|
+
[HeadingSize.XS]: 'text-sm',
|
|
113
134
|
[HeadingSize.SM]: 'text-sm',
|
|
114
135
|
[HeadingSize.MD]: 'text-base',
|
|
115
136
|
[HeadingSize.LG]: 'text-base',
|
|
@@ -120,6 +141,8 @@ const overtitleSizeClass = computed(() => {
|
|
|
120
141
|
|
|
121
142
|
const descriptionSizeClass = computed(() => {
|
|
122
143
|
const map = {
|
|
144
|
+
[HeadingSize.XXS]: 'text-sm',
|
|
145
|
+
[HeadingSize.XS]: 'text-sm',
|
|
123
146
|
[HeadingSize.SM]: 'text-sm',
|
|
124
147
|
[HeadingSize.MD]: 'text-lg',
|
|
125
148
|
[HeadingSize.LG]: 'text-lg',
|
|
@@ -130,6 +153,8 @@ const descriptionSizeClass = computed(() => {
|
|
|
130
153
|
|
|
131
154
|
const spaceOvertitleClass = computed(() => {
|
|
132
155
|
const map = {
|
|
156
|
+
[HeadingSize.XXS]: 'mb-1',
|
|
157
|
+
[HeadingSize.XS]: 'mb-1',
|
|
133
158
|
[HeadingSize.SM]: 'mb-1',
|
|
134
159
|
[HeadingSize.MD]: 'mb-3',
|
|
135
160
|
[HeadingSize.LG]: 'mb-3',
|
|
@@ -140,12 +165,16 @@ const spaceOvertitleClass = computed(() => {
|
|
|
140
165
|
|
|
141
166
|
const spaceDescriptionClass = computed(() => {
|
|
142
167
|
const normal = {
|
|
168
|
+
[HeadingSize.XXS]: 'mt-3',
|
|
169
|
+
[HeadingSize.XS]: 'mt-3',
|
|
143
170
|
[HeadingSize.SM]: 'mt-3',
|
|
144
171
|
[HeadingSize.MD]: 'mt-4',
|
|
145
172
|
[HeadingSize.LG]: 'mt-6',
|
|
146
173
|
[HeadingSize.XL]: 'mt-4',
|
|
147
174
|
}
|
|
148
175
|
const spaced = {
|
|
176
|
+
[HeadingSize.XXS]: 'mt-4',
|
|
177
|
+
[HeadingSize.XS]: 'mt-4',
|
|
149
178
|
[HeadingSize.SM]: 'mt-4',
|
|
150
179
|
[HeadingSize.MD]: 'mt-5',
|
|
151
180
|
[HeadingSize.LG]: 'mt-9',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<span
|
|
3
3
|
:class="[
|
|
4
|
-
|
|
4
|
+
colorClass,
|
|
5
5
|
'text-sm',
|
|
6
6
|
'font-medium',
|
|
7
7
|
isUppercase && 'uppercase',
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</template>
|
|
13
13
|
<script setup lang="ts">
|
|
14
14
|
// Props
|
|
15
|
-
defineProps({
|
|
15
|
+
const props = defineProps({
|
|
16
16
|
title: {
|
|
17
17
|
type: String as PropType<string>,
|
|
18
18
|
default: 'Overtitle'
|
|
@@ -20,6 +20,32 @@ defineProps({
|
|
|
20
20
|
isUppercase: {
|
|
21
21
|
type: Boolean as PropType<boolean>,
|
|
22
22
|
default: true,
|
|
23
|
+
},
|
|
24
|
+
color: {
|
|
25
|
+
type: String as PropType<
|
|
26
|
+
ColorAccent.PRIMARY_BRAND
|
|
27
|
+
| ColorAccent.SECONDARY_BRAND
|
|
28
|
+
| ColorAccent.NEUTRAL
|
|
29
|
+
>,
|
|
30
|
+
default: ColorAccent.SECONDARY_BRAND,
|
|
31
|
+
validator: (value: unknown): value is ColorAccent => {
|
|
32
|
+
return typeof value === 'string' &&
|
|
33
|
+
[
|
|
34
|
+
ColorAccent.PRIMARY_BRAND,
|
|
35
|
+
ColorAccent.SECONDARY_BRAND,
|
|
36
|
+
ColorAccent.NEUTRAL,
|
|
37
|
+
].includes(value as ColorAccent)
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const colorClass = computed(() => {
|
|
43
|
+
const variant: Partial<Record<ColorAccent, string>> = {
|
|
44
|
+
[ColorAccent.NEUTRAL]: 'text-text-default',
|
|
45
|
+
[ColorAccent.PRIMARY_BRAND]: 'text-text-primary-brand-default',
|
|
46
|
+
[ColorAccent.SECONDARY_BRAND]: 'text-text-secondary-brand-default',
|
|
23
47
|
}
|
|
48
|
+
|
|
49
|
+
return variant[props.color as ColorAccent] || 'text-text-secondary-brand-default'
|
|
24
50
|
})
|
|
25
51
|
</script>
|
|
@@ -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
|
-
|
|
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
|
-
|
|
159
|
-
type: String as PropType<
|
|
160
|
-
default:
|
|
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
|
-
|
|
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,13 @@ 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
|
-
|
|
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
|
hasGoBackLink: {
|
|
71
71
|
type: Boolean as PropType<boolean>,
|
|
72
72
|
default: false,
|
|
@@ -79,6 +79,14 @@ defineProps({
|
|
|
79
79
|
type: String as PropType<string>,
|
|
80
80
|
default: '',
|
|
81
81
|
},
|
|
82
|
+
hasSeparator: {
|
|
83
|
+
type: Boolean as PropType<boolean>,
|
|
84
|
+
default: true,
|
|
85
|
+
},
|
|
86
|
+
hasSidePadding: {
|
|
87
|
+
type: Boolean as PropType<boolean>,
|
|
88
|
+
default: true,
|
|
89
|
+
},
|
|
82
90
|
})
|
|
83
91
|
|
|
84
92
|
// Page title
|
package/models/enums/buttons.ts
CHANGED
package/models/enums/headings.ts
CHANGED
package/models/enums/pages.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<header
|
|
3
|
-
:class="[
|
|
4
|
-
'flex',
|
|
5
|
-
'items-center',
|
|
6
|
-
'justify-between',
|
|
7
|
-
'pt-12',
|
|
8
|
-
'pb-8',
|
|
9
|
-
'gap-3',
|
|
10
|
-
'px-content-side-padding-mobile md:px-content-side-padding',
|
|
11
|
-
]"
|
|
12
|
-
>
|
|
13
|
-
<div class="flex gap-4">
|
|
14
|
-
<ActionIconButton
|
|
15
|
-
:icon="isMobileSidebarOpen ? 'mdiMenuOpen' : 'mdiMenuClose'"
|
|
16
|
-
class="lg:hidden shadow-sm"
|
|
17
|
-
@click="toggleMobileSidebar"
|
|
18
|
-
/>
|
|
19
|
-
<PageTitle class="hidden lg:flex" :type="pageTitleType"/>
|
|
20
|
-
</div>
|
|
21
|
-
<div class="flex gap-3 items-center xs:w-auto">
|
|
22
|
-
<slot name="header-actions" />
|
|
23
|
-
</div>
|
|
24
|
-
</header>
|
|
25
|
-
|
|
26
|
-
</template>
|
|
27
|
-
|
|
28
|
-
<script setup lang="ts">
|
|
29
|
-
// Imports
|
|
30
|
-
import { useRoute } from 'vue-router'
|
|
31
|
-
|
|
32
|
-
// Props
|
|
33
|
-
defineProps({
|
|
34
|
-
pageTitleType: {
|
|
35
|
-
type: String as PropType<PageTitleType>,
|
|
36
|
-
default: PageTitleType.SIMPLE
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
// Page title
|
|
41
|
-
const route = useRoute()
|
|
42
|
-
const currentPageTitle = computed<string>(() =>
|
|
43
|
-
(route.meta.title as string) || 'Page title'
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
// Dynamically set the page title
|
|
47
|
-
useHead(() => ({
|
|
48
|
-
title: pageTitle(currentPageTitle.value, App.NAME),
|
|
49
|
-
}))
|
|
50
|
-
|
|
51
|
-
// Composables
|
|
52
|
-
const { isMobileSidebarOpen, toggleMobileSidebar } = useMobileSidebar()
|
|
53
|
-
|
|
54
|
-
</script>
|