barbican-reset 3.34.0 → 3.35.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.
@@ -0,0 +1,168 @@
1
+ <template>
2
+ <form class="br-select-form" @submit.prevent>
3
+ <button
4
+ class="br-select-form-button btn btn-select-form"
5
+ data-placeholder="placeholder"
6
+ @click="formButtonClick"
7
+ aria-controls="listbox"
8
+ aria-haspopup="listbox"
9
+ aria-expanded="false"
10
+ data-title="title"
11
+ role="combobox">
12
+ <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
+ </button>
23
+ <ul class="br-select-form-list" role="listbox">
24
+ <slot />
25
+ </ul>
26
+ </form>
27
+ </template>
28
+
29
+ <script setup>
30
+ import constrainTabbing from '#scripts/helpers/constrainTabbing'
31
+
32
+ let listenConstrainTabbing = null
33
+
34
+ function formSelectors($form) {
35
+ let $formButton = $form.querySelector('.br-select-form-button')
36
+ let $formList = $form.querySelector('.br-select-form-list')
37
+ let $formItems = $formList.querySelectorAll('.br-select-form-item')
38
+ let ariaExpanded = $formButton.getAttribute('aria-expanded')
39
+
40
+ return {
41
+ $formButton,
42
+ $formList,
43
+ $formItems,
44
+ isOpen: ariaExpanded == 'true',
45
+ }
46
+ }
47
+
48
+ function closeForm($form) {
49
+ let { $formButton } = formSelectors($form)
50
+
51
+ $formButton.setAttribute('aria-expanded', 'false')
52
+
53
+ document.removeEventListener('click', listenForClose)
54
+
55
+ document.removeEventListener('keydown', listenForClose)
56
+
57
+ if (listenConstrainTabbing) {
58
+ listenConstrainTabbing.stop()
59
+ listenConstrainTabbing = null
60
+ }
61
+ }
62
+
63
+ function closeForms(match) {
64
+ let $forms = document.querySelectorAll('.br-select-form')
65
+
66
+ $forms.forEach(function ($form) {
67
+ if ($form !== match) {
68
+ closeForm($form)
69
+ }
70
+ })
71
+ }
72
+
73
+ function listenForClose(event) {
74
+ let { target, key } = event
75
+
76
+ let $form = target.closest('.br-select-form')
77
+
78
+ let clickedOutsideForm = !key && !$form
79
+
80
+ let pressedEscape = ['Escape'].includes(key)
81
+
82
+ let pressedTab = ['Tab'].includes(key)
83
+
84
+ if (clickedOutsideForm || pressedEscape) {
85
+ closeForms()
86
+ }
87
+
88
+ if (!clickedOutsideForm) {
89
+ if (pressedTab && !listenConstrainTabbing) {
90
+ listenConstrainTabbing = constrainTabbing($form)
91
+ }
92
+ }
93
+ }
94
+
95
+ function formButtonClick(event) {
96
+ let { currentTarget } = event
97
+
98
+ let $form = currentTarget.closest('.br-select-form')
99
+
100
+ let { isOpen } = formSelectors($form)
101
+
102
+ currentTarget.setAttribute('aria-expanded', isOpen ? 'false' : 'true')
103
+
104
+ closeForms($form)
105
+
106
+ document.addEventListener('click', listenForClose)
107
+
108
+ document.addEventListener('keydown', listenForClose)
109
+ }
110
+
111
+ defineProps({
112
+ title: {
113
+ type: String,
114
+ default: 'title',
115
+ },
116
+ placeholder: {
117
+ type: String,
118
+ default: 'Add to wallet',
119
+ },
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
+ })
140
+ </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>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <li class="br-select-form-item">
3
+ <slot />
4
+ </li>
5
+ </template>
6
+
7
+ <style scoped>
8
+ .br-select-form-item {
9
+ border-bottom: 1px solid currentColor;
10
+ }
11
+ </style>
package/index.js CHANGED
@@ -33,6 +33,8 @@ import BrFormPassword from '#components/BrFormPassword.vue'
33
33
  import BrFormRadio from '#components/BrFormRadio.vue'
34
34
  import BrFormRadioGroup from '#components/BrFormRadioGroup.vue'
35
35
  import BrFormRow from '#components/BrFormRow.vue'
36
+ import BrFormSelect from '#components/BrFormSelect.vue'
37
+ import BrFormSelectItem from '#components/BrFormSelectItem.vue'
36
38
  import BrFormTel from '#components/BrFormTel.vue'
37
39
  import BrFormTextarea from '#components/BrFormTextarea.vue'
38
40
  import BrFormToggle from '#components/BrFormToggle.vue'
@@ -81,6 +83,8 @@ export {
81
83
  BrFormRadio,
82
84
  BrFormRadioGroup,
83
85
  BrFormRow,
86
+ BrFormSelect,
87
+ BrFormSelectItem,
84
88
  BrFormTel,
85
89
  BrFormTextarea,
86
90
  BrFormToggle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barbican-reset",
3
- "version": "3.34.0",
3
+ "version": "3.35.0",
4
4
  "description": "Shared design system for Barbican projects, providing SCSS utilities, animations, icons, Vue components, and JS helpers for consistent styling and behaviour.",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -7,21 +7,21 @@
7
7
  * rest of the page while the element is active.
8
8
  *
9
9
  * @param {HTMLElement} element - The container element to trap focus within.
10
- * @returns {void}
10
+ * @returns {{ stop: () => void }}
11
11
  *
12
12
  * @example
13
- * constrainTabbing(document.getElementById('modal'))
13
+ * const trap = constrainTabbing(document.getElementById('modal'))
14
+ * trap.stop()
14
15
  */
15
16
  export default function (element) {
16
17
  let focusableEls = element.querySelectorAll('a[href]:not([disabled], [rel=noopener]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])')
17
18
  let firstFocusableEl = focusableEls[0]
18
19
  let lastFocusableEl = focusableEls[focusableEls.length - 1]
19
- let KEYCODE_TAB = 9
20
20
 
21
- element.addEventListener('keydown', function (event) {
22
- let { key, keyCode, shiftKey } = event
21
+ function handler(/** @type {KeyboardEvent} */ event) {
22
+ let { key, shiftKey } = event
23
23
 
24
- var isTabPressed = key === 'Tab' || keyCode === KEYCODE_TAB
24
+ var isTabPressed = key === 'Tab'
25
25
 
26
26
  if (!isTabPressed) return
27
27
 
@@ -36,5 +36,13 @@ export default function (element) {
36
36
  event.preventDefault()
37
37
  }
38
38
  }
39
- })
39
+ }
40
+
41
+ element.addEventListener('keydown', handler)
42
+
43
+ return {
44
+ stop() {
45
+ element.removeEventListener('keydown', handler)
46
+ },
47
+ }
40
48
  }
@@ -221,6 +221,10 @@ button.btn:active {
221
221
  @include btn-manage-order;
222
222
  }
223
223
 
224
+ &.btn-select-form {
225
+ @include btn-select-form;
226
+ }
227
+
224
228
  &.btn-add-to-wallet {
225
229
  @include btn-add-to-wallet;
226
230
  }
@@ -0,0 +1,15 @@
1
+ .br-form-select-link {
2
+ padding: var(--padding-md);
3
+ display: block;
4
+ }
5
+
6
+ .br-form-select-link:focus-visible,
7
+ .br-form-select-link:hover {
8
+ background-color: var(--color-black-5-lighten);
9
+ }
10
+
11
+ .br-form-select-link:focus-visible {
12
+ outline-offset: var(--outline-inset-lg);
13
+ outline-color: inherit;
14
+ outline-style: dashed;
15
+ }
@@ -384,6 +384,8 @@
384
384
 
385
385
  // Navlink offset. Negative `--border-width-sm` to align active indicator flush with edge.
386
386
  --offset-navlink: calc(var(--border-width-sm) * -1);
387
+ // Large outline inset (`-4px`).
388
+ --outline-inset-lg: calc(0.25rem * -1);
387
389
  // Small outline offset (`1px`).
388
390
  --outline-offset-sm: 0.0625rem;
389
391
  // Medium outline offset (`2px`).
package/scss/index.scss CHANGED
@@ -11,6 +11,7 @@
11
11
  @use "br-form-label";
12
12
  @use "br-form-radio";
13
13
  @use "br-form-row";
14
+ @use "br-form-select-link";
14
15
  @use "br-form-textarea";
15
16
  @use "br-form-toggle";
16
17
  @use "br-form-update";
@@ -11,7 +11,6 @@
11
11
  border-width: var(--border-width-sm);
12
12
  background-color: white;
13
13
  border-style: solid;
14
- overflow: hidden;
15
14
  }
16
15
 
17
16
  @mixin br-card {
@@ -17,6 +17,7 @@
17
17
  @forward "custom/exit-overlay";
18
18
  @forward "custom/invisible";
19
19
  @forward "custom/manage-order";
20
+ @forward "custom/select-form";
20
21
  @forward "custom/outline-alert-neutral";
21
22
  @forward "custom/outline-primary";
22
23
  @forward "custom/outline-secondary";
@@ -9,7 +9,7 @@
9
9
  }
10
10
 
11
11
  &:hover,
12
- &:focus {
12
+ &:focus-visible {
13
13
  background-color: var(--color-brand-generic-15-lighten);
14
14
  border-color: var(--color-brand-generic-90-darken);
15
15
  color: var(--color-brand-generic-90-darken);
@@ -0,0 +1,24 @@
1
+ @use "./manage-order" as *;
2
+
3
+ @mixin btn-select-form {
4
+ @include btn-manage-order;
5
+
6
+ & {
7
+ justify-content: space-between;
8
+ width: 100%;
9
+ }
10
+
11
+ &[aria-expanded='true'] {
12
+ border-bottom-right-radius: 0;
13
+ border-bottom-left-radius: 0;
14
+ }
15
+
16
+ &:active {
17
+ transform: scale(1);
18
+ }
19
+
20
+ &:hover,
21
+ &:focus-visible {
22
+ fill: currentColor;
23
+ }
24
+ }