barbican-reset 3.36.0 → 3.40.0

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 (120) hide show
  1. package/components/BrAlert.vue +2 -2
  2. package/components/BrButton.vue +11 -3
  3. package/components/BrCard.vue +1 -1
  4. package/components/BrCardTearoff.vue +1 -1
  5. package/components/BrCollapse/Button.vue +1 -1
  6. package/components/BrConfirmDone.vue +6 -6
  7. package/components/BrConfirmEmail.vue +6 -6
  8. package/components/BrDetails.vue +5 -1
  9. package/components/BrFormCheckbox.vue +3 -3
  10. package/components/BrFormCheckboxGroup.vue +1 -1
  11. package/components/BrFormDate.vue +1 -1
  12. package/components/BrFormEdit.vue +2 -2
  13. package/components/BrFormEmail.vue +1 -1
  14. package/components/BrFormInput.vue +18 -1
  15. package/components/BrFormPassword.vue +2 -2
  16. package/components/BrFormRadio.vue +3 -3
  17. package/components/BrFormRadioGroup.vue +2 -2
  18. package/components/BrFormRow.vue +2 -2
  19. package/components/BrFormSelect.vue +3 -57
  20. package/components/BrFormSelectItem.vue +1 -7
  21. package/components/BrFormTel.vue +1 -1
  22. package/components/BrFormTextarea.vue +1 -1
  23. package/components/BrFormToggle.vue +6 -9
  24. package/components/BrFormUpdate.vue +1 -1
  25. package/components/BrFormVisible.vue +3 -3
  26. package/components/BrLink.vue +20 -5
  27. package/components/BrLoader.vue +1 -1
  28. package/components/BrOverlay.vue +57 -23
  29. package/components/BrQrCode.vue +21 -0
  30. package/icons/{city_of_london_lockup.vue → CityOfLondonLockup.vue} +5 -12
  31. package/icons/Dropdown.vue +6 -0
  32. package/icons/Renew.vue +5 -0
  33. package/icons/dots.vue +50 -0
  34. package/icons/index.js +18 -18
  35. package/index.js +52 -102
  36. package/package.json +8 -14
  37. package/scss/_atomic.scss +1 -1
  38. package/scss/_br-button.scss +13 -19
  39. package/scss/_br-form-select.scss +52 -0
  40. package/scss/_br-form-toggle.scss +27 -24
  41. package/scss/_br-overlay.scss +29 -4
  42. package/scss/_br-qr-code.scss +3 -0
  43. package/scss/_br-table-row.scss +1 -1
  44. package/scss/_input.scss +1 -1
  45. package/scss/_variables.scss +176 -22
  46. package/scss/card/_membership-new.scss +10 -23
  47. package/scss/index.scss +2 -1
  48. package/scss/mixins/_br-alert.scss +1 -1
  49. package/scss/mixins/_br-card.scss +1 -1
  50. package/scss/mixins/_br-form-radio.scss +1 -1
  51. package/scss/mixins/_content.scss +1 -1
  52. package/scss/mixins/_focus.scss +1 -1
  53. package/scss/mixins/_font-size.scss +3 -3
  54. package/scss/mixins/buttons/_custom.scss +12 -85
  55. package/scss/mixins/buttons/_outline.scss +8 -12
  56. package/scss/mixins/buttons/_setup.scss +3 -7
  57. package/scss/mixins/buttons/custom/_edit-input.scss +22 -0
  58. package/scss/mixins/buttons/custom/_exit-overlay-bg.scss +12 -0
  59. package/scss/mixins/buttons/custom/_exit-overlay.scss +3 -19
  60. package/scss/mixins/buttons/custom/_exit.scss +11 -0
  61. package/scss/mixins/buttons/custom/_invisible.scss +2 -2
  62. package/scss/mixins/buttons/custom/_link.scss +19 -0
  63. package/scss/mixins/buttons/custom/_manage-order.scss +4 -4
  64. package/scss/mixins/buttons/custom/_outline-secondary.scss +1 -5
  65. package/scss/mixins/buttons/custom/_renew-membership.scss +3 -3
  66. package/scss/mixins/buttons/custom/_select-form.scss +15 -5
  67. package/scss/mixins/buttons/custom/_voucher-amount.scss +18 -0
  68. package/scss/mixins/card/_membership.scss +10 -49
  69. package/scss/mixins/card/_qrcode.scss +60 -3
  70. package/scss/mixins/input/_checkbox.scss +1 -1
  71. package/scss/mixins/input/_generic.scss +1 -1
  72. package/scss/mixins/input/_select.scss +1 -1
  73. package/scss/mixins/input/_text.scss +1 -1
  74. package/scss/mixins/table/_details.scss +1 -1
  75. package/scss/mixins/table/_etickets.scss +1 -1
  76. package/scss/mixins/table/_gifts.scss +1 -1
  77. package/scss/mixins/table/_orders.scss +1 -1
  78. package/scss/mixins/table/_preferences.scss +1 -1
  79. package/scss/mixins/table/_tickets.scss +1 -1
  80. package/scss/reset.scss +4 -0
  81. package/utils/datetimes.js +162 -0
  82. package/utils/numbers.js +23 -0
  83. package/utils.js +6 -0
  84. package/components/BrAnchor.vue +0 -34
  85. package/components/BrButton/dot_typing.vue +0 -60
  86. package/components/BrButton/remove_ticket.vue +0 -46
  87. package/icons/account/index.js +0 -20
  88. package/icons/confirm/index.js +0 -4
  89. package/icons/password/index.js +0 -4
  90. package/icons/qr_code.vue +0 -5
  91. package/icons/stream/airplay.vue +0 -15
  92. package/icons/stream/cast.vue +0 -13
  93. package/icons/stream/chat.vue +0 -15
  94. package/icons/stream/cog.vue +0 -13
  95. package/icons/stream/index.js +0 -15
  96. package/icons/stream/live.vue +0 -12
  97. package/icons/stream/volume.vue +0 -13
  98. package/icons/wheelchair.vue +0 -18
  99. package/logos/arts-council-england.svg +0 -1
  100. package/logos/barbican.svg +0 -1
  101. package/logos/city-of-london.svg +0 -1
  102. package/logos/lso.svg +0 -1
  103. package/scripts/helpers.js +0 -6
  104. package/scss/_br-form-select-link.scss +0 -15
  105. /package/{scripts/animations → animations}/confirm.js +0 -0
  106. /package/icons/{account/contact.vue → AccountContact.vue} +0 -0
  107. /package/icons/{account/edit.vue → AccountEdit.vue} +0 -0
  108. /package/icons/{account/membership.vue → AccountMembership.vue} +0 -0
  109. /package/icons/{account/orders.vue → AccountOrders.vue} +0 -0
  110. /package/icons/{account/payment.vue → AccountPayment.vue} +0 -0
  111. /package/icons/{person.vue → AccountPerson.vue} +0 -0
  112. /package/icons/{account/personal.vue → AccountPersonal.vue} +0 -0
  113. /package/icons/{account/ticket.vue → AccountTicket.vue} +0 -0
  114. /package/icons/{back_arrow.vue → BackArrow.vue} +0 -0
  115. /package/icons/{city_of_london.vue → CityOfLondon.vue} +0 -0
  116. /package/icons/{information.vue → Info.vue} +0 -0
  117. /package/{scripts/helpers → utils}/compareArrays.js +0 -0
  118. /package/{scripts/helpers → utils}/constrainTabbing.js +0 -0
  119. /package/{scripts/helpers → utils}/formatKebabCase.js +0 -0
  120. /package/{scripts/helpers → utils}/logObject.js +0 -0
@@ -19,8 +19,8 @@
19
19
 
20
20
  `
21
21
  <script>
22
- import CloseIcon from "#icons/close.vue";
23
- import BrButton from "#components/BrButton.vue";
22
+ import CloseIcon from '../icons/Close.vue';
23
+ import BrButton from './BrButton.vue';
24
24
 
25
25
  export default {
26
26
  components: {
@@ -7,13 +7,13 @@
7
7
  aria-live="polite"
8
8
  ref="button">
9
9
  <template v-if="state == 'default'">
10
- <slot name="default">default</slot>
10
+ <slot name="default" />
11
11
  </template>
12
12
  <template v-else-if="state == 'loading'">
13
13
  <template v-if="$slots.loading">
14
14
  <slot name="loading">loading</slot>
15
15
  </template>
16
- <dot-typing v-else />
16
+ <dots-animation v-else />
17
17
  </template>
18
18
  <template v-else-if="state == 'loaded'">
19
19
  <slot name="loaded">loaded</slot>
@@ -26,7 +26,7 @@
26
26
 
27
27
  <script setup>
28
28
  import { computed, useTemplateRef } from 'vue'
29
- import DotTyping from '#components/BrButton/dot_typing.vue'
29
+ import DotsAnimation from '../icons/Dots.vue'
30
30
 
31
31
  const emits = defineEmits(['click'])
32
32
 
@@ -43,6 +43,10 @@ const generateClassnames = computed(() => {
43
43
  classnames.push('btn-slim')
44
44
  }
45
45
 
46
+ if (!props.noReact) {
47
+ classnames.push('btn-react')
48
+ }
49
+
46
50
  return classnames
47
51
  })
48
52
 
@@ -69,6 +73,10 @@ const props = defineProps({
69
73
  type: Boolean,
70
74
  default: false,
71
75
  },
76
+ noReact: {
77
+ type: Boolean,
78
+ default: false,
79
+ },
72
80
  })
73
81
 
74
82
  defineExpose({
@@ -16,7 +16,7 @@
16
16
 
17
17
  <script setup>
18
18
  import { computed } from 'vue'
19
- import BrCardBody from '#components/BrCardBody.vue'
19
+ import BrCardBody from './BrCardBody.vue'
20
20
 
21
21
  const generateClassnames = computed(() => {
22
22
  let classnames = ['br-card']
@@ -7,5 +7,5 @@
7
7
  </template>
8
8
 
9
9
  <script setup>
10
- import BrCardBody from '#components/BrCardBody.vue'
10
+ import BrCardBody from './BrCardBody.vue'
11
11
  </script>
@@ -10,7 +10,7 @@
10
10
  </template>
11
11
 
12
12
  <script>
13
- import BrButton from "#components/BrButton.vue";
13
+ import BrButton from "../BrButton.vue";
14
14
 
15
15
  export default {
16
16
  components: {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <br-card class="confirm-done">
3
- <done-icon />
3
+ <br-done-icon />
4
4
  <br-card-title class="title">Done</br-card-title>
5
5
  <br-card-text v-if="$slots.default" class="margin-top-1">
6
6
  <slot />
@@ -10,12 +10,12 @@
10
10
 
11
11
  <script setup>
12
12
  import { onMounted } from 'vue'
13
- import { animateDone } from '#scripts/animations/confirm'
13
+ import { animateDone } from '../animations/confirm'
14
14
 
15
- import BrCard from '#components/BrCard.vue'
16
- import BrCardTitle from '#components/BrCardTitle.vue'
17
- import BrCardText from '#components/BrCardText.vue'
18
- import DoneIcon from '#icons/confirm/done.vue'
15
+ import BrCard from './BrCard.vue'
16
+ import BrCardTitle from './BrCardTitle.vue'
17
+ import BrCardText from './BrCardText.vue'
18
+ import BrDoneIcon from '../icons/Confirm/Done.vue'
19
19
 
20
20
  onMounted(() => animateDone())
21
21
  </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <br-card class="confirm-email">
3
- <email-icon />
3
+ <br-email-icon />
4
4
  <br-card-title v-if="$slots.title" class="title">
5
5
  <slot name="title" />
6
6
  </br-card-title>
@@ -12,12 +12,12 @@
12
12
 
13
13
  <script setup>
14
14
  import { onMounted } from 'vue'
15
- import { animateEmail } from '#scripts/animations/confirm'
15
+ import { animateEmail } from '../animations/confirm'
16
16
 
17
- import BrCard from '#components/BrCard.vue'
18
- import BrCardTitle from '#components/BrCardTitle.vue'
19
- import BrCardText from '#components/BrCardText.vue'
20
- import EmailIcon from '#icons/confirm/email.vue'
17
+ import BrCard from './BrCard.vue'
18
+ import BrCardTitle from './BrCardTitle.vue'
19
+ import BrCardText from './BrCardText.vue'
20
+ import BrEmailIcon from '../icons/Confirm/Email.vue'
21
21
 
22
22
  onMounted(() => animateEmail())
23
23
  </script>
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <div class="br-details">
3
- <br-button variant="summary" @click="update" :data-test="'toggle-' + label">
3
+ <br-button
4
+ :data-test="'toggle-' + label"
5
+ variant="summary"
6
+ @click="update"
7
+ no-react>
4
8
  <div class="wrap-title">
5
9
  <slot name="title">Title goes here</slot>
6
10
  </div>
@@ -28,9 +28,9 @@
28
28
 
29
29
  <script setup>
30
30
  import { computed, useAttrs } from 'vue'
31
- import formatKebabCase from '#helpers/formatKebabCase'
32
- import BrFormLabel from '#components/BrFormLabel.vue'
33
- import BrFormInput from '#components/BrFormInput.vue'
31
+ import formatKebabCase from '../utils/formatKebabCase'
32
+ import BrFormLabel from './BrFormLabel.vue'
33
+ import BrFormInput from './BrFormInput.vue'
34
34
 
35
35
  /** All fallthrough attributes from the parent. */
36
36
  const attrs = useAttrs()
@@ -18,7 +18,7 @@
18
18
  </template>
19
19
 
20
20
  <script setup>
21
- import BrFormCheckbox from '#components/BrFormCheckbox.vue'
21
+ import BrFormCheckbox from './BrFormCheckbox.vue'
22
22
 
23
23
  const model = defineModel()
24
24
 
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup>
6
- import BrFormInput from '#components/BrFormInput.vue'
6
+ import BrFormInput from './BrFormInput.vue'
7
7
 
8
8
  defineOptions({
9
9
  inheritAttrs: false,
@@ -12,8 +12,8 @@
12
12
  </template>
13
13
 
14
14
  <script>
15
- import BrButton from '#components/BrButton.vue'
16
- import EditIcon from '#icons/account/edit.vue'
15
+ import BrButton from './BrButton.vue'
16
+ import EditIcon from '../icons/AccountEdit.vue'
17
17
 
18
18
  export default {
19
19
  props: {
@@ -3,5 +3,5 @@
3
3
  </template>
4
4
 
5
5
  <script setup>
6
- import BrFormInput from '#components/BrFormInput.vue'
6
+ import BrFormInput from './BrFormInput.vue'
7
7
  </script>
@@ -6,12 +6,13 @@
6
6
  :type="generateType"
7
7
  v-model="model"
8
8
  v-bind="$attrs"
9
+ :style="generateStyles"
9
10
  :id="id" />
10
11
  </template>
11
12
 
12
13
  <script setup>
13
14
  import { computed } from 'vue'
14
- import formatKebabCase from '#helpers/formatKebabCase'
15
+ import formatKebabCase from '../utils/formatKebabCase'
15
16
 
16
17
  const model = defineModel()
17
18
 
@@ -20,6 +21,7 @@ const props = defineProps({
20
21
  type: String,
21
22
  dataTest: String,
22
23
  autocomplete: String,
24
+ inputCustomColors: Object,
23
25
  })
24
26
 
25
27
  defineOptions({
@@ -56,6 +58,21 @@ const generateDataTest = computed(() => {
56
58
  return result && formatKebabCase(result)
57
59
  })
58
60
 
61
+ const generateStyles = computed(function () {
62
+ const colors = props.inputCustomColors
63
+
64
+ if (!colors) return
65
+
66
+ return Object.fromEntries(
67
+ [
68
+ ['--background-color-toggle-unchecked', colors.default],
69
+ ['--background-color-toggle-checked', colors.checked],
70
+ ['--background-color-toggle-before-unchecked', colors.defaultCircle],
71
+ ['--background-color-toggle-before-checked', colors.checkedCircle],
72
+ ].filter(([, value]) => value),
73
+ )
74
+ })
75
+
59
76
  // @description Returns a string for the "type" attribute
60
77
  // @returns {string} result - string based on preceeding conditions
61
78
 
@@ -12,8 +12,8 @@
12
12
 
13
13
  <script setup>
14
14
  import { ref, computed } from 'vue'
15
- import BrFormInput from '#components/BrFormInput.vue'
16
- import BrFormVisible from '#components/BrFormVisible.vue'
15
+ import BrFormInput from './BrFormInput.vue'
16
+ import BrFormVisible from './BrFormVisible.vue'
17
17
 
18
18
  const props = defineProps({
19
19
  toggle: Boolean,
@@ -20,10 +20,10 @@
20
20
 
21
21
  <script setup>
22
22
  import { computed } from 'vue'
23
- import formatKebabCase from '#helpers/formatKebabCase'
23
+ import formatKebabCase from '../utils/formatKebabCase'
24
24
 
25
- import BrFormLabel from '#components/BrFormLabel.vue'
26
- import BrFormInput from '#components/BrFormInput.vue'
25
+ import BrFormLabel from './BrFormLabel.vue'
26
+ import BrFormInput from './BrFormInput.vue'
27
27
 
28
28
  const generateClassnames = computed(() => {
29
29
  let classnames = ['br-form-radio']
@@ -17,8 +17,8 @@
17
17
  </template>
18
18
 
19
19
  <script setup>
20
- import BrFormRadio from '#components/BrFormRadio.vue'
21
- import BrWrap from '#components/BrWrap.vue'
20
+ import BrFormRadio from './BrFormRadio.vue'
21
+ import BrWrap from './BrWrap.vue'
22
22
 
23
23
  const model = defineModel()
24
24
 
@@ -16,8 +16,8 @@
16
16
 
17
17
  <script setup>
18
18
  import { computed } from 'vue'
19
- import formatKebabCase from '#helpers/formatKebabCase'
20
- import BrFormLabel from '#components/BrFormLabel.vue'
19
+ import formatKebabCase from '../utils/formatKebabCase'
20
+ import BrFormLabel from './BrFormLabel.vue'
21
21
 
22
22
  const props = defineProps({
23
23
  label: String,
@@ -9,16 +9,8 @@
9
9
  aria-expanded="false"
10
10
  data-title="title"
11
11
  role="combobox">
12
+ <br-dropdown-icon />
12
13
  <span class="br-select-form-button--title">{{ placeholder }}</span>
13
- <svg
14
- xmlns="http://www.w3.org/2000/svg"
15
- class="br-select-form-icon"
16
- width="12"
17
- height="6"
18
- version="1.1"
19
- viewBox="0 0 12 6">
20
- <path d="M6,5.48L1.04.52h9.92l-4.96,4.96Z" />
21
- </svg>
22
14
  </button>
23
15
  <ul class="br-select-form-list" role="listbox">
24
16
  <slot />
@@ -27,7 +19,8 @@
27
19
  </template>
28
20
 
29
21
  <script setup>
30
- import constrainTabbing from '#scripts/helpers/constrainTabbing'
22
+ import BrDropdownIcon from '../icons/Dropdown.vue'
23
+ import constrainTabbing from '../utils/constrainTabbing'
31
24
 
32
25
  let listenConstrainTabbing = null
33
26
 
@@ -117,52 +110,5 @@ defineProps({
117
110
  type: String,
118
111
  default: 'Add to wallet',
119
112
  },
120
- // options: {
121
- // type: Array,
122
- // default: [
123
- // {
124
- // link: {
125
- // href: 'https://apple.com',
126
- // target: '_blank',
127
- // title: 'apple',
128
- // },
129
- // },
130
- // {
131
- // link: {
132
- // href: 'https://google.com',
133
- // target: '_blank',
134
- // title: 'google',
135
- // },
136
- // },
137
- // ],
138
- // },
139
113
  })
140
114
  </script>
141
-
142
- <style scoped>
143
- .br-select-form {
144
- position: relative;
145
- width: 200px;
146
- }
147
-
148
- .br-select-form-button[aria-expanded='false'] + .br-select-form-list {
149
- visibility: hidden;
150
- }
151
-
152
- .br-select-form-list {
153
- border-bottom: 1px solid currentColor;
154
- border-right: 1px solid currentColor;
155
- border-left: 1px solid currentColor;
156
- border-bottom-right-radius: 0.5rem;
157
- border-bottom-left-radius: 0.5rem;
158
- background-color: white;
159
- position: absolute;
160
- margin-bottom: 0;
161
- list-style: none;
162
- overflow: hidden;
163
- padding-left: 0;
164
- margin-top: 0;
165
- width: 100%;
166
- z-index: 2;
167
- }
168
- </style>
@@ -2,10 +2,4 @@
2
2
  <li class="br-select-form-item">
3
3
  <slot />
4
4
  </li>
5
- </template>
6
-
7
- <style scoped>
8
- .br-select-form-item {
9
- border-bottom: 1px solid currentColor;
10
- }
11
- </style>
5
+ </template>
@@ -3,5 +3,5 @@
3
3
  </template>
4
4
 
5
5
  <script setup>
6
- import BrFormInput from '#components/BrFormInput.vue'
6
+ import BrFormInput from './BrFormInput.vue'
7
7
  </script>
@@ -8,7 +8,7 @@
8
8
 
9
9
  <script setup>
10
10
  import { computed } from 'vue'
11
- import formatKebabCase from '#helpers/formatKebabCase'
11
+ import formatKebabCase from '../utils/formatKebabCase'
12
12
 
13
13
  const value = defineModel()
14
14
 
@@ -1,18 +1,12 @@
1
1
  <template>
2
2
  <div :class="generateClasses">
3
- <BrFormLabel>
4
- <span>
5
- <slot />
6
- </span>
7
- <BrFormInput v-bind="$attrs" type="checkbox" />
8
- </BrFormLabel>
3
+ <BrFormInput v-bind="$attrs" type="checkbox" />
9
4
  </div>
10
5
  </template>
11
6
 
12
7
  <script setup>
13
8
  import { computed } from 'vue'
14
- import BrFormLabel from '#components/BrFormLabel.vue'
15
- import BrFormInput from '#components/BrFormInput.vue'
9
+ import BrFormInput from './BrFormInput.vue'
16
10
 
17
11
  defineOptions({
18
12
  inheritAttrs: false,
@@ -20,6 +14,7 @@ defineOptions({
20
14
 
21
15
  const props = defineProps({
22
16
  drawer: Boolean,
17
+ inputCustomClass: String,
23
18
  })
24
19
 
25
20
  const generateClasses = computed(() => {
@@ -27,7 +22,9 @@ const generateClasses = computed(() => {
27
22
 
28
23
  if (props.drawer) result.push('drawer')
29
24
 
30
- if (!props.drawer) result.push('default')
25
+ if (props.inputCustomClass) result.push(props.inputCustomClass)
26
+
27
+ if (!props.drawer && !props.inputCustomClass) result.push('default')
31
28
 
32
29
  return result
33
30
  })
@@ -9,7 +9,7 @@
9
9
 
10
10
  <script setup>
11
11
  import { computed } from 'vue'
12
- import BrButton from '#components/BrButton.vue'
12
+ import BrButton from './BrButton.vue'
13
13
 
14
14
  const props = defineProps({
15
15
  active: Boolean,
@@ -13,9 +13,9 @@
13
13
  </template>
14
14
 
15
15
  <script setup>
16
- import BrButton from '#components/BrButton.vue'
17
- import HidePasswordIcon from '#icons/password/hide.vue'
18
- import ShowPasswordIcon from '#icons/password/show.vue'
16
+ import BrButton from './BrButton.vue'
17
+ import HidePasswordIcon from '../icons/Password/Hide.vue'
18
+ import ShowPasswordIcon from '../icons/Password/Show.vue'
19
19
 
20
20
  const props = defineProps({
21
21
  visible: Boolean,
@@ -1,16 +1,28 @@
1
1
  <template>
2
- <a v-if="isExternal" :href="to" :class="generateClassnames">
3
- <slot />
2
+ <a
3
+ v-if="isExternal"
4
+ :href="to"
5
+ :class="generateClassnames"
6
+ @click="animateDots">
7
+ <slot v-if="!showDots" />
8
+ <dots-animation v-else />
4
9
  </a>
5
- <router-link v-else :to="to" :class="generateClassnames">
6
- <slot />
10
+ <router-link v-else :to="to" :class="generateClassnames" @click="animateDots">
11
+ <slot v-if="!showDots" />
12
+ <dots-animation v-else />
7
13
  </router-link>
8
14
  </template>
9
15
 
10
16
  <script setup>
11
17
  // https://vueschool.io/lessons/extending-router-link-for-external-urls
12
18
 
13
- import { computed } from 'vue'
19
+ import DotsAnimation from '../icons/Dots.vue'
20
+
21
+ import { computed, ref } from 'vue'
22
+
23
+ const showDots = ref(false)
24
+
25
+ const animateDots = () => (showDots.value = props.animate)
14
26
 
15
27
  const isExternal = computed(() => typeof props.to == 'string')
16
28
 
@@ -44,5 +56,8 @@ const props = defineProps({
44
56
  active: {
45
57
  type: Boolean,
46
58
  },
59
+ animate: {
60
+ type: Boolean,
61
+ },
47
62
  })
48
63
  </script>
@@ -12,7 +12,7 @@
12
12
  </template>
13
13
 
14
14
  <script>
15
- import BrCard from "#components/BrCard.vue";
15
+ import BrCard from "./BrCard.vue";
16
16
 
17
17
  export default {
18
18
  components: {
@@ -1,40 +1,74 @@
1
1
  <template>
2
- <div class="br-overlay-wrap">
3
- <div class="br-overlay-content">
4
- <slot />
2
+ <Transition name="fade">
3
+ <div v-if="show" class="br-overlay-wrap">
4
+ <div class="br-overlay-content">
5
+ <slot />
6
+ </div>
7
+ <div class="br-overlay-background">
8
+ <div class="wrap-overlay-exit-button">
9
+ <br-button variant="exit-overlay" @click="emit('closeOverlay')">
10
+ <close-icon class="close-icon" />
11
+ </br-button>
12
+ </div>
13
+ <br-button variant="exit-overlay-bg" @click="emit('closeOverlay')" />
14
+ </div>
5
15
  </div>
6
- <br-button variant="exit-overlay" @click="emit('closeOverlay')">
7
- <close-icon class="close-icon" />
8
- </br-button>
9
- </div>
16
+ </Transition>
10
17
  </template>
11
18
 
12
19
  <script setup>
13
- import { onMounted, onUnmounted } from 'vue'
14
- import { constrainTabbing } from '#scripts/helpers'
15
- import BrButton from '#components/BrButton.vue'
16
- import CloseIcon from '#icons/close.vue'
20
+ /**
21
+ * @component BrOverlay
22
+ * @description Modal overlay with a fade transition, focus trap, and Escape-to-close.
23
+ *
24
+ * @prop {Boolean} show - Controls overlay visibility via v-if.
25
+ * @emits closeOverlay - Fired when the user clicks the close button, the background, or presses Escape.
26
+ *
27
+ * @example
28
+ * <BrOverlay :show="isOpen" @closeOverlay="isOpen = false">
29
+ * <p>Content here</p>
30
+ * </BrOverlay>
31
+ */
32
+
33
+ import { onUnmounted, watch, nextTick, Transition } from 'vue'
34
+
35
+ const props = defineProps({
36
+ /** Controls overlay visibility. */
37
+ show: Boolean
38
+ })
39
+ import constrainTabbing from '../utils/constrainTabbing'
40
+ import BrButton from './BrButton.vue'
41
+ import CloseIcon from '../icons/Close.vue'
17
42
 
18
43
  const emit = defineEmits(['closeOverlay'])
19
44
 
20
- function constrainOverlayPadding() {
21
- let $overlay = document.querySelector('.br-overlay-wrap')
22
- constrainTabbing($overlay)
23
- }
45
+ /** @type {{ stop: () => void } | null} */
46
+ let tabTrap = null
24
47
 
25
- function listenForKeys({ key }) {
26
- if (key == 'Escape') {
27
- emit('closeOverlay')
48
+ // Set up / tear down the focus trap whenever the overlay opens or closes.
49
+ // nextTick ensures the v-if element is in the DOM before querying it.
50
+ watch(() => props.show, async (val) => {
51
+ if (val) {
52
+ await nextTick()
53
+ const $overlay = document.querySelector('.br-overlay-wrap')
54
+ if ($overlay) tabTrap = constrainTabbing($overlay)
55
+ } else {
56
+ tabTrap?.stop()
57
+ tabTrap = null
28
58
  }
59
+ })
60
+
61
+ /**
62
+ * @param {KeyboardEvent} event
63
+ */
64
+ function listenForKeys({ key }) {
65
+ if (key == 'Escape') emit('closeOverlay')
29
66
  }
30
67
 
31
- onMounted(() => {
32
- document.addEventListener('keydown', listenForKeys)
33
- document.addEventListener('keydown', constrainOverlayPadding)
34
- })
68
+ document.addEventListener('keydown', listenForKeys)
35
69
 
36
70
  onUnmounted(() => {
37
71
  document.removeEventListener('keydown', listenForKeys)
38
- document.removeEventListener('keydown', constrainOverlayPadding)
72
+ tabTrap?.stop()
39
73
  })
40
74
  </script>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <div class="br-qr-code" v-html="qrCode" />
3
+ </template>
4
+
5
+ <script setup>
6
+ import { ref, watchEffect } from 'vue'
7
+ import * as qrcode from 'qrcode'
8
+
9
+ const props = defineProps({
10
+ code: {
11
+ type: String,
12
+ required: true,
13
+ },
14
+ })
15
+
16
+ const qrCode = ref('')
17
+
18
+ watchEffect(async () => {
19
+ qrCode.value = await qrcode.toString(props.code, { type: 'svg', margin: 0 })
20
+ })
21
+ </script>