@sbc-connect/nuxt-base 0.0.0-test-ld-20260520212549

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.
Files changed (179) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +338 -0
  3. package/LICENSE +28 -0
  4. package/README.md +55 -0
  5. package/app/app.config.ts +441 -0
  6. package/app/app.vue +18 -0
  7. package/app/assets/css/connect-base-layout.css +13 -0
  8. package/app/assets/css/connect-base-tw.css +303 -0
  9. package/app/components/Connect/Address/Display.vue +84 -0
  10. package/app/components/Connect/BCGovLogo.vue +42 -0
  11. package/app/components/Connect/Breadcrumb.vue +68 -0
  12. package/app/components/Connect/ButtonControl/Buttons.vue +28 -0
  13. package/app/components/Connect/ButtonControl/Index.vue +66 -0
  14. package/app/components/Connect/ButtonControl/Wrapper.vue +18 -0
  15. package/app/components/Connect/Contact/Display.vue +65 -0
  16. package/app/components/Connect/FloatingLabel.vue +67 -0
  17. package/app/components/Connect/Footer.vue +99 -0
  18. package/app/components/Connect/Header/LogoHomeLink.vue +20 -0
  19. package/app/components/Connect/Header/WhatsNew.vue +36 -0
  20. package/app/components/Connect/Header/Wrapper.vue +14 -0
  21. package/app/components/Connect/Header/index.vue +17 -0
  22. package/app/components/Connect/I18n/Helper.vue +40 -0
  23. package/app/components/Connect/Input.vue +69 -0
  24. package/app/components/Connect/InputDate.vue +104 -0
  25. package/app/components/Connect/Layout/Index.vue +25 -0
  26. package/app/components/Connect/Layout/Sidebar.vue +38 -0
  27. package/app/components/Connect/LocaleSelect.vue +37 -0
  28. package/app/components/Connect/Modal/Error.vue +76 -0
  29. package/app/components/Connect/Modal/index.vue +51 -0
  30. package/app/components/Connect/PageSection.vue +57 -0
  31. package/app/components/Connect/Select.vue +28 -0
  32. package/app/components/Connect/Slideover/WhatsNew.vue +61 -0
  33. package/app/components/Connect/Spinner.vue +91 -0
  34. package/app/components/Connect/SystemBanner.vue +53 -0
  35. package/app/components/Connect/Textarea.vue +27 -0
  36. package/app/components/Connect/Tombstone/Item.vue +51 -0
  37. package/app/components/Connect/Tombstone/index.vue +123 -0
  38. package/app/components/Connect/Transition/Collapse.vue +39 -0
  39. package/app/components/Connect/Transition/Fade.vue +31 -0
  40. package/app/components/HelloWorld/Index.vue +14 -0
  41. package/app/composables/useConnectButtonControl.ts +146 -0
  42. package/app/composables/useConnectLaunchDarkly.ts +173 -0
  43. package/app/composables/useConnectModal.ts +13 -0
  44. package/app/composables/useConnectTombstone.ts +25 -0
  45. package/app/coverage/app/app.config.ts.html +1408 -0
  46. package/app/coverage/app/app.vue.html +139 -0
  47. package/app/coverage/app/components/Connect/Address/Display.vue.html +337 -0
  48. package/app/coverage/app/components/Connect/Address/index.html +116 -0
  49. package/app/coverage/app/components/Connect/BCGovLogo.vue.html +211 -0
  50. package/app/coverage/app/components/Connect/Breadcrumb.vue.html +289 -0
  51. package/app/coverage/app/components/Connect/ButtonControl/Buttons.vue.html +166 -0
  52. package/app/coverage/app/components/Connect/ButtonControl/Index.vue.html +283 -0
  53. package/app/coverage/app/components/Connect/ButtonControl/Wrapper.vue.html +139 -0
  54. package/app/coverage/app/components/Connect/ButtonControl/index.html +146 -0
  55. package/app/coverage/app/components/Connect/Contact/Display.vue.html +280 -0
  56. package/app/coverage/app/components/Connect/Contact/index.html +116 -0
  57. package/app/coverage/app/components/Connect/FloatingLabel.vue.html +286 -0
  58. package/app/coverage/app/components/Connect/Footer.vue.html +382 -0
  59. package/app/coverage/app/components/Connect/Header/LogoHomeLink.vue.html +145 -0
  60. package/app/coverage/app/components/Connect/Header/WhatsNew.vue.html +193 -0
  61. package/app/coverage/app/components/Connect/Header/Wrapper.vue.html +127 -0
  62. package/app/coverage/app/components/Connect/Header/index.html +161 -0
  63. package/app/coverage/app/components/Connect/Header/index.vue.html +136 -0
  64. package/app/coverage/app/components/Connect/I18n/Helper.vue.html +205 -0
  65. package/app/coverage/app/components/Connect/I18n/index.html +116 -0
  66. package/app/coverage/app/components/Connect/Input.vue.html +262 -0
  67. package/app/coverage/app/components/Connect/InputDate.vue.html +406 -0
  68. package/app/coverage/app/components/Connect/Layout/Index.vue.html +160 -0
  69. package/app/coverage/app/components/Connect/Layout/Sidebar.vue.html +199 -0
  70. package/app/coverage/app/components/Connect/Layout/index.html +131 -0
  71. package/app/coverage/app/components/Connect/LocaleSelect.vue.html +196 -0
  72. package/app/coverage/app/components/Connect/Modal/Error.vue.html +313 -0
  73. package/app/coverage/app/components/Connect/Modal/index.html +131 -0
  74. package/app/coverage/app/components/Connect/Modal/index.vue.html +238 -0
  75. package/app/coverage/app/components/Connect/PageSection.vue.html +256 -0
  76. package/app/coverage/app/components/Connect/Select.vue.html +169 -0
  77. package/app/coverage/app/components/Connect/Slideover/WhatsNew.vue.html +268 -0
  78. package/app/coverage/app/components/Connect/Slideover/index.html +116 -0
  79. package/app/coverage/app/components/Connect/Spinner.vue.html +358 -0
  80. package/app/coverage/app/components/Connect/SystemBanner.vue.html +244 -0
  81. package/app/coverage/app/components/Connect/Textarea.vue.html +160 -0
  82. package/app/coverage/app/components/Connect/Tombstone/Item.vue.html +238 -0
  83. package/app/coverage/app/components/Connect/Tombstone/index.html +131 -0
  84. package/app/coverage/app/components/Connect/Tombstone/index.vue.html +454 -0
  85. package/app/coverage/app/components/Connect/Transition/Collapse.vue.html +202 -0
  86. package/app/coverage/app/components/Connect/Transition/Fade.vue.html +178 -0
  87. package/app/coverage/app/components/Connect/Transition/index.html +131 -0
  88. package/app/coverage/app/components/Connect/index.html +281 -0
  89. package/app/coverage/app/components/HelloWorld/Index.vue.html +127 -0
  90. package/app/coverage/app/components/HelloWorld/index.html +116 -0
  91. package/app/coverage/app/composables/index.html +161 -0
  92. package/app/coverage/app/composables/useConnectButtonControl.ts.html +523 -0
  93. package/app/coverage/app/composables/useConnectLaunchDarkly.ts.html +595 -0
  94. package/app/coverage/app/composables/useConnectModal.ts.html +124 -0
  95. package/app/coverage/app/composables/useConnectTombstone.ts.html +160 -0
  96. package/app/coverage/app/error.vue.html +247 -0
  97. package/app/coverage/app/index.html +146 -0
  98. package/app/coverage/app/interfaces/connect-address.ts.html +121 -0
  99. package/app/coverage/app/interfaces/connect-breadcrumb.ts.html +100 -0
  100. package/app/coverage/app/interfaces/connect-button-control.ts.html +151 -0
  101. package/app/coverage/app/interfaces/connect-contact-item.ts.html +103 -0
  102. package/app/coverage/app/interfaces/connect-modal.ts.html +100 -0
  103. package/app/coverage/app/interfaces/connect-tombstone.ts.html +208 -0
  104. package/app/coverage/app/interfaces/connect-whatsnew.ts.html +127 -0
  105. package/app/coverage/app/interfaces/index.html +206 -0
  106. package/app/coverage/app/layouts/ConnectBase.vue.html +100 -0
  107. package/app/coverage/app/layouts/index.html +116 -0
  108. package/app/coverage/app/plugins/index.html +146 -0
  109. package/app/coverage/app/plugins/maska.client.ts.html +115 -0
  110. package/app/coverage/app/plugins/sanitize.client.ts.html +151 -0
  111. package/app/coverage/app/plugins/whats-new.client.ts.html +220 -0
  112. package/app/coverage/app/types/dates.ts.html +136 -0
  113. package/app/coverage/app/types/index.html +116 -0
  114. package/app/coverage/app/utils/appendUrlParam.ts.html +97 -0
  115. package/app/coverage/app/utils/getErrorMessage.ts.html +211 -0
  116. package/app/coverage/app/utils/getErrorStatus.ts.html +196 -0
  117. package/app/coverage/app/utils/index.html +191 -0
  118. package/app/coverage/app/utils/logFetchError.ts.html +193 -0
  119. package/app/coverage/app/utils/parseSpecialCharacters.ts.html +160 -0
  120. package/app/coverage/app/utils/setBreadcrumbs.ts.html +100 -0
  121. package/app/coverage/base.css +224 -0
  122. package/app/coverage/block-navigation.js +87 -0
  123. package/app/coverage/clover.xml +2094 -0
  124. package/app/coverage/coverage-final.json +58 -0
  125. package/app/coverage/favicon.png +0 -0
  126. package/app/coverage/index.html +386 -0
  127. package/app/coverage/prettify.css +1 -0
  128. package/app/coverage/prettify.js +2 -0
  129. package/app/coverage/sort-arrow-sprite.png +0 -0
  130. package/app/coverage/sorter.js +210 -0
  131. package/app/error.vue +54 -0
  132. package/app/interfaces/connect-address.ts +12 -0
  133. package/app/interfaces/connect-breadcrumb.ts +5 -0
  134. package/app/interfaces/connect-button-control.ts +23 -0
  135. package/app/interfaces/connect-contact-item.ts +6 -0
  136. package/app/interfaces/connect-modal.ts +5 -0
  137. package/app/interfaces/connect-tombstone.ts +41 -0
  138. package/app/interfaces/connect-whatsnew.ts +14 -0
  139. package/app/layouts/ConnectBase.vue +5 -0
  140. package/app/plugins/maska.client.ts +10 -0
  141. package/app/plugins/sanitize.client.ts +22 -0
  142. package/app/plugins/whats-new.client.ts +45 -0
  143. package/app/spa-loading-template.html +61 -0
  144. package/app/types/base-app-config.d.ts +15 -0
  145. package/app/types/base-directives.d.ts +7 -0
  146. package/app/types/base-page-meta.d.ts +8 -0
  147. package/app/types/dates.ts +17 -0
  148. package/app/utils/appendUrlParam.ts +4 -0
  149. package/app/utils/getErrorMessage.ts +42 -0
  150. package/app/utils/getErrorStatus.ts +37 -0
  151. package/app/utils/logFetchError.ts +36 -0
  152. package/app/utils/parseSpecialCharacters.ts +25 -0
  153. package/app/utils/setBreadcrumbs.ts +7 -0
  154. package/firebase.json +34 -0
  155. package/i18n/locales/en-CA.ts +70 -0
  156. package/i18n/locales/fr-CA.ts +96 -0
  157. package/nuxt.config.ts +147 -0
  158. package/package.json +49 -0
  159. package/public/BCGovLogo/gov_bc_logo_horiz_en.png +0 -0
  160. package/public/BCGovLogo/gov_bc_logo_horiz_fr.png +0 -0
  161. package/public/BCGovLogo/gov_bc_logo_vert_en.png +0 -0
  162. package/public/BCGovLogo/gov_bc_logo_vert_fr.png +0 -0
  163. package/public/favicon.ico +0 -0
  164. package/public/fonts/BCSans/BCSans-Bold.woff +0 -0
  165. package/public/fonts/BCSans/BCSans-Bold.woff2 +0 -0
  166. package/public/fonts/BCSans/BCSans-BoldItalic.woff +0 -0
  167. package/public/fonts/BCSans/BCSans-BoldItalic.woff2 +0 -0
  168. package/public/fonts/BCSans/BCSans-Italic.woff +0 -0
  169. package/public/fonts/BCSans/BCSans-Italic.woff2 +0 -0
  170. package/public/fonts/BCSans/BCSans-Regular.woff +0 -0
  171. package/public/fonts/BCSans/BCSans-Regular.woff2 +0 -0
  172. package/public/fonts/BCSans/LICENSE_OFL.txt +94 -0
  173. package/test-results/.last-run.json +7 -0
  174. package/test-results/error-page-404-Error-vue-D-1eb51-cted-visuals-with-404-error-Desktop-Chrome/error-context.md +135 -0
  175. package/test-results/error-page-404-Error-vue-D-1eb51-cted-visuals-with-404-error-Desktop-Chrome/test-failed-1.png +0 -0
  176. package/test-results/error-page-404-Error-vue-D-1eb51-cted-visuals-with-404-error-Desktop-Chrome/trace.zip +0 -0
  177. package/test-results/error-page-500-Error-vue-D-8c2cf-cted-visuals-with-500-error-Desktop-Chrome/error-context.md +135 -0
  178. package/test-results/error-page-500-Error-vue-D-8c2cf-cted-visuals-with-500-error-Desktop-Chrome/test-failed-1.png +0 -0
  179. package/test-results/error-page-500-Error-vue-D-8c2cf-cted-visuals-with-500-error-Desktop-Chrome/trace.zip +0 -0
@@ -0,0 +1,67 @@
1
+ <script setup lang="ts">
2
+ type VariantOption = 'input' | 'textarea' | 'select' | 'selectmenu'
3
+
4
+ const {
5
+ variant = 'input'
6
+ } = defineProps<{
7
+ label: string
8
+ variant?: VariantOption
9
+ id: string
10
+ }>()
11
+
12
+ const variantClass: Record<VariantOption, string[]> = {
13
+ input: [
14
+ '-translate-y-1/2 top-1/2 group-focus-within:top-3 delay-50',
15
+ 'group-has-[div>input:not(:placeholder-shown)]:top-3',
16
+ 'group-has-[div>input:not(:placeholder-shown)]:text-xs',
17
+ 'group-has-[div>input[aria-invalid=true]]:text-error'
18
+ ],
19
+ textarea: [
20
+ 'top-1 group-focus-within:top-1 delay-50',
21
+ 'group-has-[div>textarea:not(:placeholder-shown)]:top-1',
22
+ 'group-has-[div>textarea:not(:placeholder-shown)]:text-xs',
23
+ 'group-has-[div>textarea[aria-invalid=true]]:text-error'
24
+ ],
25
+ select: [
26
+ 'delay-100',
27
+ '-translate-y-1/2 top-1/2 group-focus-within:top-3',
28
+ 'group-has-[button[aria-invalid=true]]:text-error',
29
+ 'group-has-[button[data-state=open]]:top-3',
30
+ 'group-has-[button[data-state=open]]:text-xs',
31
+ 'group-has-[button:not([data-placeholder])]:top-3',
32
+ 'group-has-[button:not([data-placeholder])]:text-xs',
33
+ 'group-has-[button[data-state=open]:not([aria-invalid=true])]:text-primary'
34
+ ],
35
+ selectmenu: [
36
+ 'delay-100',
37
+ '-translate-y-1/2 top-1/2 group-focus-within:top-3',
38
+ 'group-has-[button[aria-invalid=true]]:text-error',
39
+ 'group-has-[button[data-state=open]]:top-3',
40
+ 'group-has-[button[data-state=open]]:text-xs',
41
+ 'group-has-[button[data-placeholder=false]]:top-3',
42
+ 'group-has-[button[data-placeholder=false]]:text-xs',
43
+ 'group-has-[button[data-state=open]:not([aria-invalid=true])]:text-primary'
44
+ ]
45
+ }
46
+ </script>
47
+
48
+ <template>
49
+ <div class="relative group">
50
+ <span
51
+ :id
52
+ :class="[
53
+ variantClass[variant],
54
+ 'line-clamp-1 px-2.5',
55
+ 'absolute z-10 group-focus-within:text-primary',
56
+ 'pointer-events-none text-neutral transition-all',
57
+ 'motion-reduce:transition-none duration-200',
58
+ 'group-focus-within:text-xs',
59
+ 'group-focus-within:text-primary',
60
+ 'w-fit',
61
+ ]"
62
+ >
63
+ {{ label }}
64
+ </span>
65
+ <slot />
66
+ </div>
67
+ </template>
@@ -0,0 +1,99 @@
1
+ <script setup lang="ts">
2
+ const rtc = useRuntimeConfig().public
3
+ const uiVersion = rtc.version
4
+ const feesUrl = `${rtc.registryHomeUrl}product-fees`
5
+ const dependencyVersions: string[] = useAppConfig().connect?.footer?.versions || []
6
+ const localePath = useLocalePath()
7
+ const links = [
8
+ {
9
+ label: 'connect.label.home',
10
+ to: localePath('/')
11
+ },
12
+ {
13
+ label: 'connect.label.fees',
14
+ to: feesUrl,
15
+ target: '_blank'
16
+ },
17
+ {
18
+ label: 'connect.label.disclaimer',
19
+ to: 'https://www2.gov.bc.ca/gov/content/home/disclaimer',
20
+ target: '_blank'
21
+ },
22
+ {
23
+ label: 'connect.label.privacy',
24
+ to: 'https://www2.gov.bc.ca/gov/content/home/privacy',
25
+ target: '_blank'
26
+ },
27
+ {
28
+ label: 'connect.label.accessibility',
29
+ to: 'https://www2.gov.bc.ca/gov/content/home/accessibility',
30
+ target: '_blank'
31
+ },
32
+ {
33
+ label: 'connect.label.copyright',
34
+ to: 'https://www2.gov.bc.ca/gov/content/home/copyright',
35
+ target: '_blank'
36
+ }
37
+ ]
38
+
39
+ const appVersions = computed<string[]>(() => {
40
+ const items = [...dependencyVersions]
41
+ if (uiVersion) {
42
+ items.unshift(uiVersion)
43
+ }
44
+ return items
45
+ })
46
+ </script>
47
+
48
+ <template>
49
+ <footer
50
+ id="connect-main-footer"
51
+ data-testid="connect-main-footer"
52
+ class="border-t-2 border-brand-inverted bg-brand p-2 sm:px-4"
53
+ >
54
+ <div class="app-inner-container flex items-center justify-between">
55
+ <nav :aria-label="$t('connect.footer.navLabel')" class="flex grow">
56
+ <ul class="list-none flex-col gap-1 p-0 sm:-ml-2 sm:flex-row sm:flex-wrap">
57
+ <li
58
+ v-for="link in links"
59
+ :key="link.to"
60
+ :class="[
61
+ 'flex-col gap-2 border-r-0 pr-2 last:mr-0',
62
+ 'last:border-r-0 sm:mr-2 sm:inline-block sm:flex-none',
63
+ 'sm:border-r sm:first:ml-2 border-line-muted',
64
+ ]"
65
+ >
66
+ <NuxtLink
67
+ :to="link.to === '/' ? `/${$i18n.locale}` : link.to"
68
+ :target="link.target"
69
+ :class="[
70
+ 'rounded p-1 text-sm text-secondary',
71
+ 'hover:underline focus:outline-none',
72
+ 'focus-visible:ring-2 focus-visible:ring-secondary',
73
+ ]"
74
+ >
75
+ {{ $t(link.label) }}
76
+ </NuxtLink>
77
+ </li>
78
+ </ul>
79
+ </nav>
80
+ <div class="-mb-1 flex items-center">
81
+ <span class="italic text-brand-inverted">{{ $t('connect.label.bcApp') }}</span>
82
+ <UTooltip arrow :delay-duration="200">
83
+ <UButton
84
+ :aria-label="$t('connect.label.appVersion')"
85
+ color="secondary"
86
+ icon="i-mdi-info-outline"
87
+ variant="ghost"
88
+ />
89
+
90
+ <template #content>
91
+ <div class="flex flex-col">
92
+ <span v-for="(item, i) in appVersions" :key="i">{{ item }}</span>
93
+ </div>
94
+ </template>
95
+ </UTooltip>
96
+ </div>
97
+ </div>
98
+ </footer>
99
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ const localePath = useLocalePath()
3
+ </script>
4
+
5
+ <template>
6
+ <NuxtLink
7
+ id="header-logo-home-link"
8
+ :to="localePath('/')"
9
+ :class="[
10
+ 'flex items-center gap-4 rounded-md',
11
+ 'px-2 transition-colors duration-300',
12
+ 'ease-in-out hover:bg-secondary/[0.1]',
13
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-secondary',
14
+ ]"
15
+ :aria-label="$t('connect.header.homeLink')"
16
+ >
17
+ <ConnectBCGovLogo />
18
+ <span class="text-base font-semibold text-secondary lg:text-lg"> {{ $t('connect.header.title') }} </span>
19
+ </NuxtLink>
20
+ </template>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import { useStorage } from '@vueuse/core'
3
+ import { ConnectSlideoverWhatsNew } from '#components'
4
+
5
+ const state = useStorage<ConnectWhatsNewState>('connect-whats-new', { viewed: false, items: [] })
6
+ const overlay = useOverlay()
7
+ const slideover = overlay.create(ConnectSlideoverWhatsNew)
8
+
9
+ function open() {
10
+ slideover.open({
11
+ items: state.value.items
12
+ })
13
+ state.value.viewed = true
14
+ }
15
+ </script>
16
+
17
+ <template>
18
+ <UChip
19
+ :show="!state.viewed && state.items.length > 0"
20
+ color="error"
21
+ position="top-left"
22
+ inset
23
+ :ui="{
24
+ base: 'ring-(--ui-error)',
25
+ }"
26
+ >
27
+ <UButton
28
+ variant="ghost"
29
+ color="secondary"
30
+ class="px-2 py-1 text-sm"
31
+ :label="$t('connect.label.whatsNew')"
32
+ :aria-label="$t('connect.label.whatsNewAria', { count: state.viewed ? 0 : state.items.length })"
33
+ @click="open"
34
+ />
35
+ </UChip>
36
+ </template>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <header
3
+ id="connect-header-wrapper"
4
+ data-testid="connect-header-wrapper"
5
+ class="relative border-b-2 border-brand-inverted bg-brand p-2 sm:px-4"
6
+ >
7
+ <nav
8
+ class="app-inner-container"
9
+ :aria-label="$t('connect.header.navLabel')"
10
+ >
11
+ <slot />
12
+ </nav>
13
+ </header>
14
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ const headerConfig = useAppConfig().connect.header
3
+ </script>
4
+
5
+ <template>
6
+ <ConnectHeaderWrapper>
7
+ <div class="flex items-center justify-between">
8
+ <ConnectHeaderLogoHomeLink />
9
+ <div class="flex gap-4">
10
+ <slot>
11
+ <ConnectHeaderWhatsNew v-if="headerConfig.whatsNew" />
12
+ <ConnectLocaleSelect v-if="headerConfig.localeSelect" />
13
+ </slot>
14
+ </div>
15
+ </div>
16
+ </ConnectHeaderWrapper>
17
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ const {
3
+ translationPath,
4
+ as = 'span'
5
+ } = defineProps<{
6
+ translationPath: string
7
+ as?: 'h1'
8
+ | 'h2'
9
+ | 'h3'
10
+ | 'h4'
11
+ | 'h5'
12
+ | 'h6'
13
+ | 'label'
14
+ | 'span'
15
+ | 'p'
16
+ | 'div'
17
+ | 'li'
18
+ | 'legend'
19
+ | 'strong'
20
+ | 'em'
21
+ }>()
22
+
23
+ const attrs = useAttrs()
24
+ const { t } = useI18n()
25
+
26
+ const textToDisplay = computed(() => {
27
+ const translationProps = {
28
+ ...attrs,
29
+ boldStart: '<strong>',
30
+ boldEnd: '</strong>',
31
+ italicStart: '<em>',
32
+ italicEnd: '</em>'
33
+ }
34
+ return t(translationPath, translationProps)
35
+ })
36
+ </script>
37
+
38
+ <template>
39
+ <component :is="as" v-sanitize="textToDisplay" />
40
+ </template>
@@ -0,0 +1,69 @@
1
+ <script setup lang="ts">
2
+ import type { InputProps } from '@nuxt/ui'
3
+
4
+ const {
5
+ id,
6
+ autoFocus = false
7
+ } = defineProps<{
8
+ id: string
9
+ label: string
10
+ mask?: string
11
+ disabled?: boolean
12
+ autoFocus?: boolean
13
+ }>()
14
+
15
+ const model = defineModel<InputProps['modelValue']>({ required: true })
16
+ const inputRef = useTemplateRef('input-ref')
17
+
18
+ // Inject props
19
+ const injectedProps = inject<InputProps>(
20
+ `UInput-props${id ? `-${id}` : ''}`,
21
+ {} as InputProps
22
+ )
23
+
24
+ // Inject slots
25
+ const injectedSlots = inject<{ [key: string]: VNode }>(
26
+ `UInput-slots${id ? `-${id}` : ''}`,
27
+ {}
28
+ )
29
+
30
+ defineExpose({ input: inputRef })
31
+
32
+ // vue ignore reason - type mismatch between model and defaultValue prop caused by strict generics
33
+ </script>
34
+
35
+ <template>
36
+ <!-- @vue-ignore -->
37
+ <UInput
38
+ ref="input-ref"
39
+ v-bind="{
40
+ id,
41
+ disabled,
42
+ autofocus: autoFocus,
43
+ ...injectedProps, // injected props will default when provided
44
+ }"
45
+ v-model.trim="model"
46
+ v-maska="mask"
47
+ class="w-full grow"
48
+ :data-testid="id"
49
+ placeholder="&nbsp;"
50
+ >
51
+ <label
52
+ :for="id"
53
+ class="floating-label-input"
54
+ >
55
+ {{ label }}
56
+ </label>
57
+
58
+ <!-- Render injected slots -->
59
+ <template
60
+ v-for="(comp, name) in injectedSlots"
61
+ #[name]
62
+ :key="name"
63
+ >
64
+ <component
65
+ :is="comp"
66
+ />
67
+ </template>
68
+ </UInput>
69
+ </template>
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ // https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/
4
+ // https://whatsock.com/Templates/Datepickers/Basic,%20Auto%20Open/index.htm
5
+ import type { ConnectInput, UCalendar } from '#components'
6
+ import { CalendarDate } from '@internationalized/date'
7
+ import { DateTime } from 'luxon'
8
+
9
+ // FUTURE: update props as needed (min/max/disabled days)
10
+ defineProps<{
11
+ label: string
12
+ id: string
13
+ }>()
14
+
15
+ const emit = defineEmits<{
16
+ blur: []
17
+ }>()
18
+
19
+ const externalModel = defineModel<string | null | undefined>({ required: true })
20
+
21
+ const localModel = computed({
22
+ get() {
23
+ if (externalModel.value) {
24
+ const dt = DateTime.fromISO(externalModel.value, { zone: 'America/Vancouver' })
25
+ if (dt.isValid) {
26
+ return new CalendarDate(dt.year, dt.month, dt.day)
27
+ }
28
+ }
29
+ return undefined
30
+ },
31
+ set(v) {
32
+ if (v) {
33
+ externalModel.value = DateTime.fromObject({
34
+ year: v.year,
35
+ month: v.month,
36
+ day: v.day
37
+ }).toISODate()
38
+ } else {
39
+ externalModel.value = null
40
+ }
41
+ }
42
+ })
43
+
44
+ const open = ref(false)
45
+ const calendarRef = useTemplateRef('calendar-ref')
46
+ const inputRef = useTemplateRef<InstanceType<typeof ConnectInput>>('input-ref')
47
+
48
+ function focusCalendar() {
49
+ const button = (calendarRef.value as any)?.$el?.querySelector('button')
50
+ if (button) {
51
+ (button as HTMLButtonElement).focus()
52
+ }
53
+ }
54
+
55
+ function focusInput() {
56
+ inputRef.value?.input?.inputRef?.focus()
57
+ open.value = false
58
+ emit('blur')
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <UPopover
64
+ v-model:open="open"
65
+ :content="{
66
+ onOpenAutoFocus: (e) => e.preventDefault(),
67
+ onEscapeKeyDown: () => {
68
+ focusInput()
69
+ },
70
+ side: 'bottom',
71
+ align: 'start',
72
+ }"
73
+ @update:open="(v) => {
74
+ if (!v) {
75
+ $emit('blur')
76
+ }
77
+ }"
78
+ >
79
+ <template #default>
80
+ <div class="w-full group">
81
+ <ConnectInput
82
+ :id
83
+ ref="input-ref"
84
+ v-model="externalModel"
85
+ :label
86
+ mask="####-##-##"
87
+ trailing-icon="i-mdi-calendar"
88
+ @focus="open = true"
89
+ @click.stop="open = true"
90
+ @keydown.down.prevent="focusCalendar"
91
+ />
92
+ </div>
93
+ </template>
94
+
95
+ <template #content>
96
+ <UCalendar
97
+ ref="calendar-ref"
98
+ v-model="localModel"
99
+ class="p-2"
100
+ @update:model-value="focusInput"
101
+ />
102
+ </template>
103
+ </UPopover>
104
+ </template>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="app-container">
3
+ <slot name="header">
4
+ <ConnectHeader />
5
+ </slot>
6
+ <slot name="banner">
7
+ <ConnectSystemBanner />
8
+ </slot>
9
+ <slot name="breadcrumb">
10
+ <ConnectBreadcrumb />
11
+ </slot>
12
+ <slot name="bottomHeaders" />
13
+ <main class="app-body px-2 sm:px-4">
14
+ <slot name="body">
15
+ <div class="app-inner-container grow flex flex-col overflow-clip">
16
+ <slot />
17
+ </div>
18
+ </slot>
19
+ </main>
20
+ <slot name="topFooters" />
21
+ <slot name="footer">
22
+ <ConnectFooter />
23
+ </slot>
24
+ </div>
25
+ </template>
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import type { NavigationMenuItem } from '@nuxt/ui'
3
+
4
+ defineProps<{ items: NavigationMenuItem[] }>()
5
+
6
+ const showSideMenu = ref(true)
7
+ </script>
8
+
9
+ <template>
10
+ <div class="flex gap-4 my-10 relative">
11
+ <UButton
12
+ class="absolute z-1 -top-8"
13
+ color="neutral"
14
+ size="xs"
15
+ :icon="showSideMenu ? 'i-mdi-minus' : 'i-mdi-plus'"
16
+ data-testid="connect-sidebar-toggle-btn"
17
+ @click="showSideMenu = !showSideMenu"
18
+ />
19
+ <ConnectTransitionCollapse>
20
+ <div v-if="showSideMenu">
21
+ <UNavigationMenu
22
+ collapsible
23
+ :items
24
+ orientation="vertical"
25
+ class="text-neutral-highlighted"
26
+ variant="pill"
27
+ :ui="{
28
+ label: 'text-base',
29
+ link: 'cursor-pointer',
30
+ linkLabel: 'text-neutral-highlighted',
31
+ root: 'bg-white rounded p-3',
32
+ }"
33
+ />
34
+ </div>
35
+ </ConnectTransitionCollapse>
36
+ <slot />
37
+ </div>
38
+ </template>
@@ -0,0 +1,37 @@
1
+ <script setup lang="ts">
2
+ import type { DropdownMenuItem } from '@nuxt/ui'
3
+ import type { LocaleObject } from '@nuxtjs/i18n'
4
+
5
+ const { locale, locales, setLocale } = useI18n()
6
+
7
+ const items = computed((): DropdownMenuItem[][] => {
8
+ const options = locales.value.map((loc: LocaleObject) => {
9
+ const isCurrentLocal = loc.code === locale.value
10
+ return {
11
+ label: loc.name || 'N/A',
12
+ icon: isCurrentLocal ? 'i-mdi-check' : '',
13
+ onSelect: () => setLocale(loc.code),
14
+ class: isCurrentLocal ? 'bg-gray-100' : 'pl-8',
15
+ color: isCurrentLocal ? 'primary' as const : 'neutral' as const
16
+ }
17
+ })
18
+ return [options]
19
+ })
20
+ </script>
21
+
22
+ <template>
23
+ <UDropdownMenu
24
+ v-if="items[0] && items[0].length > 1"
25
+ :items
26
+ color="secondary"
27
+ >
28
+ <UButton
29
+ data-testid="locale-select-dropdown"
30
+ icon="i-mdi-web"
31
+ :aria-label="$t('connect.label.localeSelect')"
32
+ size="lg"
33
+ variant="ghost"
34
+ color="secondary"
35
+ />
36
+ </UDropdownMenu>
37
+ </template>
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ import type { VNode } from 'vue'
3
+ // https://vue-i18n.intlify.dev/api/composition.html#te-key-locale
4
+ const { t, te } = useI18n()
5
+
6
+ interface ErrorModalProps {
7
+ error: unknown
8
+ i18nPrefix: string
9
+ buttons?: ConnectModalButton[]
10
+ contactInfo?: VNode
11
+ preferErrorMessage?: boolean
12
+ }
13
+
14
+ const {
15
+ error,
16
+ i18nPrefix,
17
+ buttons = [
18
+ { label: useNuxtApp().$i18n.t('label.close'), shouldClose: true }
19
+ ],
20
+ preferErrorMessage = false
21
+ } = defineProps<ErrorModalProps>()
22
+ defineEmits<{ close: [] }>()
23
+
24
+ const status = getErrorStatus(error)
25
+ const message = getErrorMessage(error)
26
+
27
+ const titleKey = `${i18nPrefix}.${status}.title`
28
+ const descKey = `${i18nPrefix}.${status}.description`
29
+
30
+ const title = te(titleKey) ? t(titleKey) : t(`${i18nPrefix}.undefined.title`)
31
+ const i18nDescription = te(descKey) ? t(descKey) : t(`${i18nPrefix}.undefined.description`)
32
+
33
+ const description = (preferErrorMessage && message)
34
+ ? message
35
+ : i18nDescription
36
+ </script>
37
+
38
+ <template>
39
+ <UModal
40
+ id="connect-error-modal"
41
+ overlay
42
+ :title
43
+ :description
44
+ :dismissible="false"
45
+ >
46
+ <template #content>
47
+ <div class="p-10 flex flex-col gap-6">
48
+ <h2 class="text-2xl font-semibold text-neutral-highlighted">
49
+ {{ title }}
50
+ </h2>
51
+ <p>{{ description }}</p>
52
+ <component :is="contactInfo" v-if="contactInfo" />
53
+ <div class="flex flex-wrap items-center justify-center gap-4">
54
+ <template
55
+ v-for="button in buttons"
56
+ :key="button.label"
57
+ >
58
+ <UButton
59
+ v-if="button.shouldClose"
60
+ v-bind="button"
61
+ size="xl"
62
+ class="w-full justify-center sm:w-min sm:justify-normal"
63
+ @click="$emit('close')"
64
+ />
65
+ <UButton
66
+ v-else
67
+ v-bind="button"
68
+ size="xl"
69
+ class="w-full justify-center sm:w-min sm:justify-normal"
70
+ />
71
+ </template>
72
+ </div>
73
+ </div>
74
+ </template>
75
+ </UModal>
76
+ </template>