@gitlab/ui 128.2.3 → 128.3.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.
@@ -27,11 +27,12 @@ var script = {
27
27
  *
28
28
  * @typedef {object} FieldDefinition
29
29
  * @template TValue=string
30
- * @property {string} label - Label text to show for this field.
30
+ * @property {string | null | undefined} label - Label text to show for this field. When explicitly set to null, the label is suppressed. When undefined or not provided, the field name is used as the label.
31
31
  * @property {undefined | Object} groupAttrs - Properties that are passed to the group wrapping this field.
32
32
  * @property {undefined | Object} inputAttrs - Properties that are passed to the actual input for this field.
33
33
  * @property {undefined | function(string): TValue} mapValue - Function that maps the inputted string value to the field's actual value (e.g. a Number).
34
34
  * @property {undefined | Array<function(TValue): string | undefined>=} validators - Collection of validator functions.
35
+ * @property {undefined | boolean} fieldset - When true, renders the form group as a fieldset with legend instead of div with label.
35
36
  *
36
37
  * @type {{ [key: string]: FieldDefinition }}
37
38
  */
@@ -130,7 +131,7 @@ var script = {
130
131
  return {
131
132
  ...field,
132
133
  id,
133
- label: field.label || fieldName,
134
+ label: this.getFieldLabel(field, fieldName),
134
135
  inputSlot,
135
136
  groupPassthroughSlots,
136
137
  afterSlotName
@@ -168,6 +169,10 @@ var script = {
168
169
  await this.$nextTick();
169
170
  return this.hasAllFieldsValid();
170
171
  },
172
+ getFieldLabel(field, fieldName) {
173
+ if (field.label === null) return undefined;
174
+ return field.label || fieldName;
175
+ },
171
176
  getMappedValue(fieldName, val) {
172
177
  const field = this.fields[fieldName];
173
178
  if (isFunction(field === null || field === void 0 ? void 0 : field.mapValue)) {
@@ -248,7 +253,7 @@ const __vue_script__ = script;
248
253
  var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-form-fields-loop',{attrs:{"fields":_vm.fieldsToRender},scopedSlots:_vm._u([{key:"default",fn:function(ref){
249
254
  var field = ref.field;
250
255
  var fieldName = ref.fieldName;
251
- return [_c('gl-form-group',_vm._b({key:field.id,attrs:{"label":field.label,"label-for":field.id,"invalid-feedback":_vm.fieldValidationProps[fieldName].invalidFeedback,"state":_vm.fieldValidationProps[fieldName].state},scopedSlots:_vm._u([_vm._l((field.groupPassthroughSlots),function(ref){
256
+ return [_c('gl-form-group',_vm._b({key:field.id,attrs:{"label":field.label,"label-for":field.fieldset ? undefined : field.id,"invalid-feedback":_vm.fieldValidationProps[fieldName].invalidFeedback,"state":_vm.fieldValidationProps[fieldName].state},scopedSlots:_vm._u([_vm._l((field.groupPassthroughSlots),function(ref){
252
257
  var slotName = ref.slotName;
253
258
  var childSlotName = ref.childSlotName;
254
259
  return {key:childSlotName,fn:function(){return [_vm._t(slotName)]},proxy:true}})],null,true)},'gl-form-group',field.groupAttrs,false),[_c('gl-form-field-validator',{attrs:{"value":_vm.fieldValues[fieldName],"validators":field.validators,"should-validate":_vm.fieldDirtyStatuses[fieldName]},on:{"update":function($event){return _vm.onFieldValidationUpdate(fieldName, $event)}}}),_vm._v(" "),_vm._v(" "),_vm._t(field.inputSlot.slotName,function(){return [_c('gl-form-input',_vm._b({attrs:{"id":field.id,"value":_vm.fieldValues[fieldName],"state":_vm.fieldValidationProps[fieldName].state},on:{"input":function($event){return _vm.onFieldInput(fieldName, $event)},"blur":function($event){return _vm.onFieldBlur(fieldName)}}},'gl-form-input',field.inputAttrs,false))]},null,field.inputSlot.attrs)],2),_vm._v(" "),_vm._t(field.afterSlotName)]}}],null,true)})};
@@ -77,7 +77,7 @@ var script = {
77
77
  const __vue_script__ = script;
78
78
 
79
79
  /* template */
80
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-form-group',_vm._b({staticClass:"gl-form-group",attrs:{"label-class":_vm.actualLabelClass},scopedSlots:_vm._u([{key:"label",fn:function(){return [_vm._t("label",function(){return [_vm._v("\n "+_vm._s(_vm.$attrs.label)+"\n "),(_vm.optional)?_c('span',{staticClass:"optional-label",attrs:{"data-testid":"optional-label"}},[_vm._v(_vm._s(_vm.optionalText))]):_vm._e()]}),_vm._v(" "),(_vm.hasLabelDescription)?_c('div',{staticClass:"label-description",attrs:{"data-testid":"label-description"}},[_vm._t("label-description",function(){return [_vm._v(_vm._s(_vm.labelDescription))]})],2):_vm._e()]},proxy:true},_vm._l((Object.keys(_vm.$slots)),function(slot){return {key:slot,fn:function(){return [_vm._t(slot)]},proxy:true}})],null,true)},'b-form-group',_vm.$attrs,false))};
80
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-form-group',_vm._b({staticClass:"gl-form-group",attrs:{"label-class":_vm.actualLabelClass},scopedSlots:_vm._u([(_vm.$attrs.label || _vm.$scopedSlots.label)?{key:"label",fn:function(){return [_vm._t("label",function(){return [_vm._v("\n "+_vm._s(_vm.$attrs.label)+"\n "),(_vm.optional)?_c('span',{staticClass:"optional-label",attrs:{"data-testid":"optional-label"}},[_vm._v(_vm._s(_vm.optionalText))]):_vm._e()]}),_vm._v(" "),(_vm.hasLabelDescription)?_c('div',{staticClass:"label-description",attrs:{"data-testid":"label-description"}},[_vm._t("label-description",function(){return [_vm._v(_vm._s(_vm.labelDescription))]})],2):_vm._e()]},proxy:true}:null,_vm._l((Object.keys(_vm.$slots)),function(slot){return {key:slot,fn:function(){return [_vm._t(slot)]},proxy:true}})],null,true)},'b-form-group',_vm.$attrs,false))};
81
81
  var __vue_staticRenderFns__ = [];
82
82
 
83
83
  /* style */
@@ -92,8 +92,8 @@ var script = {
92
92
  }
93
93
  return [];
94
94
  },
95
- close(e) {
96
- this.$refs[popoverRefName].doClose();
95
+ closePopover(e) {
96
+ this.$refs[popoverRefName].close();
97
97
  /**
98
98
  * Emitted when the close button is clicked (requires showCloseButton to be `true`).
99
99
  */
@@ -107,7 +107,7 @@ var script = {
107
107
  const __vue_script__ = script;
108
108
 
109
109
  /* template */
110
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-popover',_vm._g(_vm._b({ref:_vm.$options.popoverRefName,attrs:{"custom-class":_vm.customClass,"triggers":_vm.triggers,"title":_vm.title,"placement":_vm.placement,"boundary-padding":_vm.boundaryPadding},scopedSlots:_vm._u([(_vm.shouldShowTitle)?{key:"title",fn:function(){return [_vm._t("title",function(){return [_vm._v("\n "+_vm._s(_vm.title)+"\n ")]}),_vm._v(" "),(_vm.showCloseButton)?_c('div',{staticClass:"-gl-mr-3 -gl-mt-2 gl-ml-3 gl-h-0"},[_c('close-button',{class:{ 'gl-float-right gl-mt-2': !_vm.hasTitle },attrs:{"data-testid":"close-button"},on:{"click":_vm.close}})],1):_vm._e()]},proxy:true}:null,(_vm.$scopedSlots.default)?{key:"default",fn:function(){return [_vm._t("default")]},proxy:true}:null],null,true)},'b-popover',_vm.$attrs,false),_vm.$listeners))};
110
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-popover',_vm._g(_vm._b({ref:_vm.$options.popoverRefName,attrs:{"custom-class":_vm.customClass,"triggers":_vm.triggers,"title":_vm.title,"placement":_vm.placement,"boundary-padding":_vm.boundaryPadding},scopedSlots:_vm._u([(_vm.shouldShowTitle)?{key:"title",fn:function(){return [_vm._t("title",function(){return [_vm._v("\n "+_vm._s(_vm.title)+"\n ")]}),_vm._v(" "),(_vm.showCloseButton)?_c('div',{staticClass:"-gl-mr-3 -gl-mt-2 gl-ml-3 gl-h-0"},[_c('close-button',{class:{ 'gl-float-right gl-mt-2': !_vm.hasTitle },attrs:{"data-testid":"close-button"},on:{"click":_vm.closePopover}})],1):_vm._e()]},proxy:true}:null,(_vm.$scopedSlots.default)?{key:"default",fn:function(){return [_vm._t("default")]},proxy:true}:null],null,true)},'b-popover',_vm.$attrs,false),_vm.$listeners))};
111
111
  var __vue_staticRenderFns__ = [];
112
112
 
113
113
  /* style */
@@ -1,4 +1,4 @@
1
- import { GlUniqueId } from '../../../utils/unique_id';
1
+ import uniqueId from 'lodash/uniqueId';
2
2
  import { tokensValidator } from './helpers';
3
3
  import GlTokenContainer from './token_container';
4
4
  import GlTokenSelectorDropdown from './token_selector_dropdown';
@@ -244,7 +244,7 @@ var script = {
244
244
  },
245
245
  created() {
246
246
  // Each instance must have a unique ID for proper ARIA relationships
247
- this.uniqueId = `token-selector-${GlUniqueId()}`;
247
+ this.uniqueId = uniqueId('token-selector-');
248
248
  },
249
249
  methods: {
250
250
  handleFocus(event) {
@@ -11,7 +11,7 @@ var tooltip_mixin = tooltipRefName => ({
11
11
  * https://bootstrap-vue.org/docs/components/popover#programmatically-show-and-hide-popover
12
12
  * https://bootstrap-vue.org/docs/components/tooltip#programmatically-show-and-hide-tooltip
13
13
  */
14
- tooltipActionEvents.forEach(event => this.$on(event, () => this.$refs[tooltipRefName].$emit(event)));
14
+ tooltipActionEvents.forEach(event => this.$on(event, () => this.$refs[tooltipRefName][event]()));
15
15
  },
16
16
  beforeDestroy() {
17
17
  tooltipActionEvents.forEach(event => this.$off(event));
@@ -1,6 +1,6 @@
1
1
  import { extend } from '../../vue';
2
2
  import { NAME_TOOLTIP } from '../../constants/components';
3
- import { EVENT_NAME_OPEN, EVENT_NAME_CLOSE, EVENT_NAME_DISABLE, EVENT_NAME_ENABLE, EVENT_NAME_SHOW, EVENT_NAME_SHOWN, EVENT_NAME_HIDE, EVENT_NAME_HIDDEN, EVENT_NAME_DISABLED, EVENT_NAME_ENABLED, MODEL_EVENT_NAME_PREFIX } from '../../constants/events';
3
+ import { EVENT_NAME_DISABLE, EVENT_NAME_ENABLE, EVENT_NAME_SHOW, EVENT_NAME_SHOWN, EVENT_NAME_HIDE, EVENT_NAME_HIDDEN, EVENT_NAME_DISABLED, EVENT_NAME_ENABLED, EVENT_NAME_OPEN, EVENT_NAME_CLOSE, MODEL_EVENT_NAME_PREFIX } from '../../constants/events';
4
4
  import { PROP_TYPE_OBJECT, PROP_TYPE_STRING, PROP_TYPE_NUMBER_STRING, PROP_TYPE_NUMBER_OBJECT_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_ARRAY_STRING, PROP_TYPE_FUNCTION } from '../../constants/props';
5
5
  import { HTMLElement, SVGElement } from '../../constants/safe-types';
6
6
  import { useParentMixin } from '../../mixins/use-parent';
@@ -103,9 +103,9 @@ const BTooltip = /*#__PURE__*/extend({
103
103
  },
104
104
  [MODEL_PROP_NAME_ENABLED](newValue) {
105
105
  if (newValue) {
106
- this.doDisable();
106
+ this[EVENT_NAME_DISABLE]();
107
107
  } else {
108
- this.doEnable();
108
+ this[EVENT_NAME_ENABLE]();
109
109
  }
110
110
  },
111
111
  localShow(newValue) {
@@ -134,11 +134,6 @@ const BTooltip = /*#__PURE__*/extend({
134
134
  this.$nextTick(this.updateContent);
135
135
  },
136
136
  beforeDestroy() {
137
- // Shutdown our local event listeners
138
- this.$off(EVENT_NAME_OPEN, this.doOpen);
139
- this.$off(EVENT_NAME_CLOSE, this.doClose);
140
- this.$off(EVENT_NAME_DISABLE, this.doDisable);
141
- this.$off(EVENT_NAME_ENABLE, this.doEnable);
142
137
  // Destroy the tip instance
143
138
  if (this.$_toolpop) {
144
139
  this.$_toolpop.$destroy();
@@ -174,16 +169,8 @@ const BTooltip = /*#__PURE__*/extend({
174
169
  // Initially disabled?
175
170
  if (this[MODEL_PROP_NAME_ENABLED]) {
176
171
  // Initially disabled
177
- this.doDisable();
172
+ this[EVENT_NAME_DISABLE]();
178
173
  }
179
- // Listen to open signals from others
180
- this.$on(EVENT_NAME_OPEN, this.doOpen);
181
- // Listen to close signals from others
182
- this.$on(EVENT_NAME_CLOSE, this.doClose);
183
- // Listen to disable signals from others
184
- this.$on(EVENT_NAME_DISABLE, this.doDisable);
185
- // Listen to enable signals from others
186
- this.$on(EVENT_NAME_ENABLE, this.doEnable);
187
174
  // Initially show tooltip?
188
175
  if (this.localShow) {
189
176
  $toolpop.show();
@@ -255,17 +242,17 @@ const BTooltip = /*#__PURE__*/extend({
255
242
  this.$emit(EVENT_NAME_ENABLED, bvEvent);
256
243
  }
257
244
  },
258
- // --- Local event listeners ---
259
- doOpen() {
245
+ // --- Public methods for programmatic control ---
246
+ [EVENT_NAME_OPEN]() {
260
247
  !this.localShow && this.$_toolpop && this.$_toolpop.show();
261
248
  },
262
- doClose() {
249
+ [EVENT_NAME_CLOSE]() {
263
250
  this.localShow && this.$_toolpop && this.$_toolpop.hide();
264
251
  },
265
- doDisable() {
252
+ [EVENT_NAME_DISABLE]() {
266
253
  this.$_toolpop && this.$_toolpop.disable();
267
254
  },
268
- doEnable() {
255
+ [EVENT_NAME_ENABLE]() {
269
256
  this.$_toolpop && this.$_toolpop.enable();
270
257
  }
271
258
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "128.2.3",
3
+ "version": "128.3.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -165,7 +165,7 @@
165
165
  "start-server-and-test": "^2.1.3",
166
166
  "storybook": "^7.6.20",
167
167
  "storybook-dark-mode": "4.0.2",
168
- "style-dictionary": "^5.1.4",
168
+ "style-dictionary": "^5.2.0",
169
169
  "style-loader": "^4",
170
170
  "tailwindcss": "3.4.19",
171
171
  "vue": "2.7.16",
@@ -27,11 +27,12 @@ export default {
27
27
  *
28
28
  * @typedef {object} FieldDefinition
29
29
  * @template TValue=string
30
- * @property {string} label - Label text to show for this field.
30
+ * @property {string | null | undefined} label - Label text to show for this field. When explicitly set to null, the label is suppressed. When undefined or not provided, the field name is used as the label.
31
31
  * @property {undefined | Object} groupAttrs - Properties that are passed to the group wrapping this field.
32
32
  * @property {undefined | Object} inputAttrs - Properties that are passed to the actual input for this field.
33
33
  * @property {undefined | function(string): TValue} mapValue - Function that maps the inputted string value to the field's actual value (e.g. a Number).
34
34
  * @property {undefined | Array<function(TValue): string | undefined>=} validators - Collection of validator functions.
35
+ * @property {undefined | boolean} fieldset - When true, renders the form group as a fieldset with legend instead of div with label.
35
36
  *
36
37
  * @type {{ [key: string]: FieldDefinition }}
37
38
  */
@@ -140,7 +141,7 @@ export default {
140
141
  return {
141
142
  ...field,
142
143
  id,
143
- label: field.label || fieldName,
144
+ label: this.getFieldLabel(field, fieldName),
144
145
  inputSlot,
145
146
  groupPassthroughSlots,
146
147
  afterSlotName,
@@ -179,6 +180,10 @@ export default {
179
180
 
180
181
  return this.hasAllFieldsValid();
181
182
  },
183
+ getFieldLabel(field, fieldName) {
184
+ if (field.label === null) return undefined;
185
+ return field.label || fieldName;
186
+ },
182
187
  getMappedValue(fieldName, val) {
183
188
  const field = this.fields[fieldName];
184
189
 
@@ -271,7 +276,7 @@ export default {
271
276
  v-bind="field.groupAttrs"
272
277
  :key="field.id"
273
278
  :label="field.label"
274
- :label-for="field.id"
279
+ :label-for="field.fieldset ? undefined : field.id"
275
280
  :invalid-feedback="fieldValidationProps[fieldName].invalidFeedback"
276
281
  :state="fieldValidationProps[fieldName].state"
277
282
  >
@@ -71,7 +71,7 @@ export default {
71
71
  </script>
72
72
  <template>
73
73
  <b-form-group v-bind="$attrs" class="gl-form-group" :label-class="actualLabelClass">
74
- <template #label>
74
+ <template v-if="$attrs.label || $scopedSlots.label" #label>
75
75
  <slot name="label">
76
76
  {{ $attrs.label }}
77
77
  <span v-if="optional" class="optional-label" data-testid="optional-label">{{
@@ -103,8 +103,8 @@ export default {
103
103
 
104
104
  return [];
105
105
  },
106
- close(e) {
107
- this.$refs[popoverRefName].doClose();
106
+ closePopover(e) {
107
+ this.$refs[popoverRefName].close();
108
108
  /**
109
109
  * Emitted when the close button is clicked (requires showCloseButton to be `true`).
110
110
  */
@@ -135,7 +135,7 @@ export default {
135
135
  <close-button
136
136
  :class="{ 'gl-float-right gl-mt-2': !hasTitle }"
137
137
  data-testid="close-button"
138
- @click="close"
138
+ @click="closePopover"
139
139
  />
140
140
  </div>
141
141
  </template>
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { GlUniqueId } from '../../../utils/unique_id';
2
+ import uniqueId from 'lodash/uniqueId';
3
3
  import { tokensValidator } from './helpers';
4
4
  import GlTokenContainer from './token_container.vue';
5
5
  import GlTokenSelectorDropdown from './token_selector_dropdown.vue';
@@ -252,7 +252,7 @@ export default {
252
252
  },
253
253
  created() {
254
254
  // Each instance must have a unique ID for proper ARIA relationships
255
- this.uniqueId = `token-selector-${GlUniqueId()}`;
255
+ this.uniqueId = uniqueId('token-selector-');
256
256
  },
257
257
  methods: {
258
258
  handleFocus(event) {
@@ -12,7 +12,7 @@ export default (tooltipRefName) => ({
12
12
  * https://bootstrap-vue.org/docs/components/tooltip#programmatically-show-and-hide-tooltip
13
13
  */
14
14
  tooltipActionEvents.forEach((event) =>
15
- this.$on(event, () => this.$refs[tooltipRefName].$emit(event)),
15
+ this.$on(event, () => this.$refs[tooltipRefName][event]()),
16
16
  );
17
17
  },
18
18
  beforeDestroy() {
@@ -141,9 +141,9 @@ export const BTooltip = /*#__PURE__*/ extend({
141
141
  },
142
142
  [MODEL_PROP_NAME_ENABLED](newValue) {
143
143
  if (newValue) {
144
- this.doDisable()
144
+ this[EVENT_NAME_DISABLE]()
145
145
  } else {
146
- this.doEnable()
146
+ this[EVENT_NAME_ENABLE]()
147
147
  }
148
148
  },
149
149
  localShow(newValue) {
@@ -172,11 +172,6 @@ export const BTooltip = /*#__PURE__*/ extend({
172
172
  this.$nextTick(this.updateContent)
173
173
  },
174
174
  beforeDestroy() {
175
- // Shutdown our local event listeners
176
- this.$off(EVENT_NAME_OPEN, this.doOpen)
177
- this.$off(EVENT_NAME_CLOSE, this.doClose)
178
- this.$off(EVENT_NAME_DISABLE, this.doDisable)
179
- this.$off(EVENT_NAME_ENABLE, this.doEnable)
180
175
  // Destroy the tip instance
181
176
  if (this.$_toolpop) {
182
177
  this.$_toolpop.$destroy()
@@ -212,16 +207,8 @@ export const BTooltip = /*#__PURE__*/ extend({
212
207
  // Initially disabled?
213
208
  if (this[MODEL_PROP_NAME_ENABLED]) {
214
209
  // Initially disabled
215
- this.doDisable()
210
+ this[EVENT_NAME_DISABLE]()
216
211
  }
217
- // Listen to open signals from others
218
- this.$on(EVENT_NAME_OPEN, this.doOpen)
219
- // Listen to close signals from others
220
- this.$on(EVENT_NAME_CLOSE, this.doClose)
221
- // Listen to disable signals from others
222
- this.$on(EVENT_NAME_DISABLE, this.doDisable)
223
- // Listen to enable signals from others
224
- this.$on(EVENT_NAME_ENABLE, this.doEnable)
225
212
  // Initially show tooltip?
226
213
  if (this.localShow) {
227
214
  $toolpop.show()
@@ -293,17 +280,17 @@ export const BTooltip = /*#__PURE__*/ extend({
293
280
  this.$emit(EVENT_NAME_ENABLED, bvEvent)
294
281
  }
295
282
  },
296
- // --- Local event listeners ---
297
- doOpen() {
283
+ // --- Public methods for programmatic control ---
284
+ [EVENT_NAME_OPEN]() {
298
285
  !this.localShow && this.$_toolpop && this.$_toolpop.show()
299
286
  },
300
- doClose() {
287
+ [EVENT_NAME_CLOSE]() {
301
288
  this.localShow && this.$_toolpop && this.$_toolpop.hide()
302
289
  },
303
- doDisable() {
290
+ [EVENT_NAME_DISABLE]() {
304
291
  this.$_toolpop && this.$_toolpop.disable()
305
292
  },
306
- doEnable() {
293
+ [EVENT_NAME_ENABLE]() {
307
294
  this.$_toolpop && this.$_toolpop.enable()
308
295
  }
309
296
  },
@@ -1,18 +0,0 @@
1
- /**
2
- * Function uses browser native crypto to return a sixteen
3
- * character base16 encoded string. It is often used to
4
- * generate collision-resistant IDs for aria markup such
5
- * as aria-activedescendant and aria-controls.
6
- *
7
- * @returns String
8
- */
9
- const GlUniqueId = () => {
10
- if (typeof window !== 'undefined' && window.crypto) {
11
- const arr32 = new Uint32Array(2);
12
- window.crypto.getRandomValues(arr32);
13
- return Array.from(arr32, id => id.toString(16)).join('');
14
- }
15
- throw new Error('Cannot generate a unique ID');
16
- };
17
-
18
- export { GlUniqueId };
@@ -1,17 +0,0 @@
1
- /**
2
- * Function uses browser native crypto to return a sixteen
3
- * character base16 encoded string. It is often used to
4
- * generate collision-resistant IDs for aria markup such
5
- * as aria-activedescendant and aria-controls.
6
- *
7
- * @returns String
8
- */
9
- export const GlUniqueId = () => {
10
- if (typeof window !== 'undefined' && window.crypto) {
11
- const arr32 = new Uint32Array(2);
12
- window.crypto.getRandomValues(arr32);
13
- return Array.from(arr32, (id) => id.toString(16)).join('');
14
- }
15
-
16
- throw new Error('Cannot generate a unique ID');
17
- };