@gitlab/ui 112.1.0 → 112.1.2

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.
@@ -1,274 +0,0 @@
1
- import { NAME_COLLAPSE } from '../../constants/components'
2
- import { IS_BROWSER } from '../../constants/env'
3
- import { EVENT_OPTIONS_PASSIVE } from '../../constants/events'
4
- import { CODE_ENTER, CODE_SPACE } from '../../constants/key-codes'
5
- import { RX_HASH, RX_HASH_ID, RX_SPACE_SPLIT } from '../../constants/regex'
6
- import { arrayIncludes, concat } from '../../utils/array'
7
- import { getInstanceFromDirective } from '../../utils/get-instance-from-directive'
8
- import {
9
- addClass,
10
- getAttr,
11
- hasAttr,
12
- isDisabled,
13
- isTag,
14
- removeAttr,
15
- removeClass,
16
- removeStyle,
17
- requestAF,
18
- setAttr,
19
- setStyle
20
- } from '../../utils/dom'
21
- import { getRootActionEventName, getRootEventName, eventOn, eventOff } from '../../utils/events'
22
- import { isString } from '../../utils/inspect'
23
- import { looseEqual } from '../../utils/loose-equal'
24
- import { keys } from '../../utils/object'
25
- import { getEventRoot } from '../../utils/get-event-root'
26
-
27
- // --- Constants ---
28
-
29
- // Classes to apply to trigger element
30
- const CLASS_BV_TOGGLE_COLLAPSED = 'collapsed'
31
- const CLASS_BV_TOGGLE_NOT_COLLAPSED = 'not-collapsed'
32
-
33
- // Property key for handler storage
34
- const BV_BASE = '__BV_toggle'
35
- // Root event listener property (Function)
36
- const BV_TOGGLE_ROOT_HANDLER = `${BV_BASE}_HANDLER__`
37
- // Trigger element click handler property (Function)
38
- const BV_TOGGLE_CLICK_HANDLER = `${BV_BASE}_CLICK__`
39
- // Target visibility state property (Boolean)
40
- const BV_TOGGLE_STATE = `${BV_BASE}_STATE__`
41
- // Target ID list property (Array)
42
- const BV_TOGGLE_TARGETS = `${BV_BASE}_TARGETS__`
43
-
44
- // Commonly used strings
45
- const STRING_FALSE = 'false'
46
- const STRING_TRUE = 'true'
47
-
48
- // Commonly used attribute names
49
- const ATTR_ARIA_CONTROLS = 'aria-controls'
50
- const ATTR_ARIA_EXPANDED = 'aria-expanded'
51
- const ATTR_ROLE = 'role'
52
- const ATTR_TABINDEX = 'tabindex'
53
-
54
- // Commonly used style properties
55
- const STYLE_OVERFLOW_ANCHOR = 'overflow-anchor'
56
-
57
- // Emitted control event for collapse (emitted to collapse)
58
- const ROOT_ACTION_EVENT_NAME_TOGGLE = getRootActionEventName(NAME_COLLAPSE, 'toggle')
59
-
60
- // Listen to event for toggle state update (emitted by collapse)
61
- const ROOT_EVENT_NAME_STATE = getRootEventName(NAME_COLLAPSE, 'state')
62
-
63
- // Private event emitted on `$root` to ensure the toggle state is always synced
64
- // Gets emitted even if the state of b-collapse has not changed
65
- // This event is NOT to be documented as people should not be using it
66
- const ROOT_EVENT_NAME_SYNC_STATE = getRootEventName(NAME_COLLAPSE, 'sync-state')
67
-
68
- // Private event we send to collapse to request state update sync event
69
- const ROOT_ACTION_EVENT_NAME_REQUEST_STATE = getRootActionEventName(NAME_COLLAPSE, 'request-state')
70
-
71
- const KEYDOWN_KEY_CODES = [CODE_ENTER, CODE_SPACE]
72
-
73
- // --- Helper methods ---
74
-
75
- const isNonStandardTag = el => !arrayIncludes(['button', 'a'], el.tagName.toLowerCase())
76
-
77
- const getTargets = ({ modifiers, arg, value }, el) => {
78
- // Any modifiers are considered target IDs
79
- const targets = keys(modifiers || {})
80
-
81
- // If value is a string, split out individual targets (if space delimited)
82
- value = isString(value) ? value.split(RX_SPACE_SPLIT) : value
83
-
84
- // Support target ID as link href (`href="#id"`)
85
- if (isTag(el.tagName, 'a')) {
86
- const href = getAttr(el, 'href') || ''
87
- if (RX_HASH_ID.test(href)) {
88
- targets.push(href.replace(RX_HASH, ''))
89
- }
90
- }
91
-
92
- // Add ID from `arg` (if provided), and support value
93
- // as a single string ID or an array of string IDs
94
- // If `value` is not an array or string, then it gets filtered out
95
- concat(arg, value).forEach(t => isString(t) && targets.push(t))
96
-
97
- // Return only unique and truthy target IDs
98
- return targets.filter((t, index, arr) => t && arr.indexOf(t) === index)
99
- }
100
-
101
- const removeClickListener = el => {
102
- const handler = el[BV_TOGGLE_CLICK_HANDLER]
103
- if (handler) {
104
- eventOff(el, 'click', handler, EVENT_OPTIONS_PASSIVE)
105
- eventOff(el, 'keydown', handler, EVENT_OPTIONS_PASSIVE)
106
- }
107
- el[BV_TOGGLE_CLICK_HANDLER] = null
108
- }
109
-
110
- const addClickListener = (el, instance) => {
111
- removeClickListener(el)
112
- if (instance) {
113
- const handler = event => {
114
- if (
115
- !(event.type === 'keydown' && !arrayIncludes(KEYDOWN_KEY_CODES, event.keyCode)) &&
116
- !isDisabled(el)
117
- ) {
118
- const targets = el[BV_TOGGLE_TARGETS] || []
119
- targets.forEach(target => {
120
- getEventRoot(instance).$emit(ROOT_ACTION_EVENT_NAME_TOGGLE, target)
121
- })
122
- }
123
- }
124
- el[BV_TOGGLE_CLICK_HANDLER] = handler
125
- eventOn(el, 'click', handler, EVENT_OPTIONS_PASSIVE)
126
- if (isNonStandardTag(el)) {
127
- eventOn(el, 'keydown', handler, EVENT_OPTIONS_PASSIVE)
128
- }
129
- }
130
- }
131
-
132
- const removeRootListeners = (el, instance) => {
133
- if (el[BV_TOGGLE_ROOT_HANDLER] && instance) {
134
- getEventRoot(instance).$off(
135
- [ROOT_EVENT_NAME_STATE, ROOT_EVENT_NAME_SYNC_STATE],
136
- el[BV_TOGGLE_ROOT_HANDLER]
137
- )
138
- }
139
- el[BV_TOGGLE_ROOT_HANDLER] = null
140
- }
141
-
142
- const addRootListeners = (el, instance) => {
143
- removeRootListeners(el, instance)
144
- if (instance) {
145
- const handler = (id, state) => {
146
- // `state` will be `true` if target is expanded
147
- if (arrayIncludes(el[BV_TOGGLE_TARGETS] || [], id)) {
148
- // Set/Clear 'collapsed' visibility class state
149
- el[BV_TOGGLE_STATE] = state
150
- // Set `aria-expanded` and class state on trigger element
151
- setToggleState(el, state)
152
- }
153
- }
154
- el[BV_TOGGLE_ROOT_HANDLER] = handler
155
- // Listen for toggle state changes (public) and sync (private)
156
- getEventRoot(instance).$on([ROOT_EVENT_NAME_STATE, ROOT_EVENT_NAME_SYNC_STATE], handler)
157
- }
158
- }
159
-
160
- const setToggleState = (el, state) => {
161
- // State refers to the visibility of the collapse
162
- if (state) {
163
- removeClass(el, CLASS_BV_TOGGLE_COLLAPSED)
164
- addClass(el, CLASS_BV_TOGGLE_NOT_COLLAPSED)
165
- setAttr(el, ATTR_ARIA_EXPANDED, STRING_TRUE)
166
- } else {
167
- removeClass(el, CLASS_BV_TOGGLE_NOT_COLLAPSED)
168
- addClass(el, CLASS_BV_TOGGLE_COLLAPSED)
169
- setAttr(el, ATTR_ARIA_EXPANDED, STRING_FALSE)
170
- }
171
- }
172
-
173
- // Reset and remove a property from the provided element
174
- const resetProp = (el, prop) => {
175
- el[prop] = null
176
- delete el[prop]
177
- }
178
-
179
- // Handle directive updates
180
- const handleUpdate = (el, binding, vnode) => {
181
- /* istanbul ignore next: should never happen */
182
- if (!IS_BROWSER || !getInstanceFromDirective(vnode, binding)) {
183
- return
184
- }
185
-
186
- // If element is not a button or link, we add `role="button"`
187
- // and `tabindex="0"` for accessibility reasons
188
- if (isNonStandardTag(el)) {
189
- if (!hasAttr(el, ATTR_ROLE)) {
190
- setAttr(el, ATTR_ROLE, 'button')
191
- }
192
- if (!hasAttr(el, ATTR_TABINDEX)) {
193
- setAttr(el, ATTR_TABINDEX, '0')
194
- }
195
- }
196
-
197
- // Ensure the collapse class and `aria-*` attributes persist
198
- // after element is updated (either by parent re-rendering
199
- // or changes to this element or its contents)
200
- setToggleState(el, el[BV_TOGGLE_STATE])
201
-
202
- // Parse list of target IDs
203
- const targets = getTargets(binding, el)
204
-
205
- // Ensure the `aria-controls` hasn't been overwritten
206
- // or removed when vnode updates
207
- // Also ensure to set `overflow-anchor` to `none` to prevent
208
- // the browser's scroll anchoring behavior
209
- /* istanbul ignore else */
210
- if (targets.length > 0) {
211
- setAttr(el, ATTR_ARIA_CONTROLS, targets.join(' '))
212
- setStyle(el, STYLE_OVERFLOW_ANCHOR, 'none')
213
- } else {
214
- removeAttr(el, ATTR_ARIA_CONTROLS)
215
- removeStyle(el, STYLE_OVERFLOW_ANCHOR)
216
- }
217
-
218
- // Add/Update our click listener(s)
219
- // Wrap in a `requestAF()` to allow any previous
220
- // click handling to occur first
221
- requestAF(() => {
222
- addClickListener(el, getInstanceFromDirective(vnode, binding))
223
- })
224
-
225
- // If targets array has changed, update
226
- if (!looseEqual(targets, el[BV_TOGGLE_TARGETS])) {
227
- // Update targets array to element storage
228
- el[BV_TOGGLE_TARGETS] = targets
229
- // Ensure `aria-controls` is up to date
230
- // Request a state update from targets so that we can
231
- // ensure expanded state is correct (in most cases)
232
- targets.forEach(target => {
233
- getEventRoot(getInstanceFromDirective(vnode, binding)).$emit(
234
- ROOT_ACTION_EVENT_NAME_REQUEST_STATE,
235
- target
236
- )
237
- })
238
- }
239
- }
240
-
241
- /*
242
- * Export our directive
243
- */
244
- export const VBToggle = {
245
- bind(el, binding, vnode) {
246
- // State is initially collapsed until we receive a state event
247
- el[BV_TOGGLE_STATE] = false
248
- // Assume no targets initially
249
- el[BV_TOGGLE_TARGETS] = []
250
- // Add our root listeners
251
- addRootListeners(el, getInstanceFromDirective(vnode, binding))
252
- // Initial update of trigger
253
- handleUpdate(el, binding, vnode)
254
- },
255
- componentUpdated: handleUpdate,
256
- updated: handleUpdate,
257
- unbind(el, binding, vnode) {
258
- removeClickListener(el)
259
- // Remove our $root listener
260
- removeRootListeners(el, getInstanceFromDirective(vnode, binding))
261
- // Reset custom props
262
- resetProp(el, BV_TOGGLE_ROOT_HANDLER)
263
- resetProp(el, BV_TOGGLE_CLICK_HANDLER)
264
- resetProp(el, BV_TOGGLE_STATE)
265
- resetProp(el, BV_TOGGLE_TARGETS)
266
- // Reset classes/attrs/styles
267
- removeClass(el, CLASS_BV_TOGGLE_COLLAPSED)
268
- removeClass(el, CLASS_BV_TOGGLE_NOT_COLLAPSED)
269
- removeAttr(el, ATTR_ARIA_EXPANDED)
270
- removeAttr(el, ATTR_ARIA_CONTROLS)
271
- removeAttr(el, ATTR_ROLE)
272
- removeStyle(el, STYLE_OVERFLOW_ANCHOR)
273
- }
274
- }