@gitlab/ui 107.6.1 → 107.7.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [107.7.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v107.6.2...v107.7.0) (2025-01-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * **Dropdown:** Add indicator ([065c810](https://gitlab.com/gitlab-org/gitlab-ui/commit/065c810e49345d97748f7c3341604641372ad5b8))
7
+
8
+ ## [107.6.2](https://gitlab.com/gitlab-org/gitlab-ui/compare/v107.6.1...v107.6.2) (2025-01-30)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **GlButtonGroup:** Fix specificity problem ([837d85c](https://gitlab.com/gitlab-org/gitlab-ui/commit/837d85c744068fc4d44301b75a6d672fac757e77))
14
+
1
15
  ## [107.6.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v107.6.0...v107.6.1) (2025-01-27)
2
16
 
3
17
 
@@ -1,12 +1,12 @@
1
1
  import uniqueId from 'lodash/uniqueId';
2
- import { offset, autoPlacement, shift, size, autoUpdate, computePosition } from '@floating-ui/dom';
2
+ import { offset, autoPlacement, shift, arrow, size, autoUpdate, computePosition } from '@floating-ui/dom';
3
3
  import { buttonCategoryOptions, dropdownVariantOptions, buttonSizeOptions, dropdownPlacements, dropdownAllowedAutoPlacements } from '../../../../utils/constants';
4
4
  import { POSITION_ABSOLUTE, POSITION_FIXED, GL_DROPDOWN_CONTENTS_CLASS, GL_DROPDOWN_BEFORE_CLOSE, GL_DROPDOWN_SHOWN, GL_DROPDOWN_HIDDEN, ENTER, SPACE, ARROW_DOWN, GL_DROPDOWN_FOCUS_CONTENT } from '../constants';
5
5
  import { logWarning, isElementFocusable, isElementTabbable } from '../../../../utils/utils';
6
6
  import { OutsideDirective } from '../../../../directives/outside/outside';
7
7
  import GlButton from '../../button/button';
8
8
  import GlIcon from '../../icon/icon';
9
- import { DEFAULT_OFFSET, FIXED_WIDTH_CLASS } from './constants';
9
+ import { DEFAULT_OFFSET, FIXED_WIDTH_CLASS, ARROW_X_MINIMUM } from './constants';
10
10
  import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
11
11
 
12
12
  const BASE_DROPDOWN_CLASS = 'gl-new-dropdown';
@@ -257,7 +257,9 @@ var script = {
257
257
  middleware: [offset(this.offset), autoPlacement({
258
258
  alignment,
259
259
  allowedPlacements: dropdownAllowedAutoPlacements[this.placement]
260
- }), shift(), size({
260
+ }), shift(), arrow({
261
+ element: this.$refs.dropdownArrow
262
+ }), size({
261
263
  apply: _ref => {
262
264
  var _this$nonScrollableCo;
263
265
  let {
@@ -265,9 +267,7 @@ var script = {
265
267
  elements
266
268
  } = _ref;
267
269
  const contentsEl = elements.floating.querySelector(`.${GL_DROPDOWN_CONTENTS_CLASS}`);
268
- if (!contentsEl) {
269
- return;
270
- }
270
+ if (!contentsEl) return;
271
271
  const contentsAvailableHeight = availableHeight - ((_this$nonScrollableCo = this.nonScrollableContentHeight) !== null && _this$nonScrollableCo !== void 0 ? _this$nonScrollableCo : 0) - DEFAULT_OFFSET;
272
272
  Object.assign(contentsEl.style, {
273
273
  maxHeight: `${Math.max(contentsAvailableHeight, 0)}px`
@@ -302,19 +302,38 @@ var script = {
302
302
  Use 'a' or 'button' element instead or make sure to add 'role="button"' along with 'tabindex' otherwise.`, this.$el);
303
303
  }
304
304
  },
305
+ getArrowOffsets(actualPlacement) {
306
+ // Try to extract the base direction (top, bottom, left, right) from the placement
307
+ const direction = actualPlacement === null || actualPlacement === void 0 ? void 0 : actualPlacement.split('-')[0];
308
+ const offsetConfigs = {
309
+ top: {
310
+ staticSide: 'bottom',
311
+ rotation: '225deg'
312
+ },
313
+ bottom: {
314
+ staticSide: 'top',
315
+ rotation: '45deg'
316
+ },
317
+ left: {
318
+ staticSide: 'right',
319
+ rotation: '135deg'
320
+ },
321
+ right: {
322
+ staticSide: 'left',
323
+ rotation: '315deg'
324
+ }
325
+ };
326
+ return offsetConfigs[direction] || offsetConfigs.bottom;
327
+ },
305
328
  async startFloating() {
306
329
  this.calculateNonScrollableAreaHeight();
307
330
  this.observer = new MutationObserver(this.calculateNonScrollableAreaHeight);
308
331
  this.observer.observe(this.$refs.content, {
309
- attributes: false,
310
332
  childList: true,
311
333
  subtree: true
312
334
  });
313
335
  this.stopAutoUpdate = autoUpdate(this.toggleElement, this.$refs.content, async () => {
314
- const {
315
- x,
316
- y
317
- } = await computePosition(this.toggleElement, this.$refs.content, this.floatingUIConfig);
336
+ const result = await computePosition(this.toggleElement, this.$refs.content, this.floatingUIConfig);
318
337
 
319
338
  /**
320
339
  * Due to the asynchronous nature of computePosition, it's technically possible for the
@@ -322,10 +341,48 @@ var script = {
322
341
  * early to prevent a TypeError.
323
342
  */
324
343
  if (!this.$refs.content) return;
344
+ const {
345
+ x,
346
+ y,
347
+ middlewareData,
348
+ placement
349
+ } = result;
350
+
351
+ // Get offsets based on actual placement, not requested placement
352
+ const {
353
+ rotation,
354
+ staticSide
355
+ } = this.getArrowOffsets(placement);
356
+
357
+ // Assign dropdown window position
325
358
  Object.assign(this.$refs.content.style, {
326
359
  left: `${x}px`,
327
360
  top: `${y}px`
328
361
  });
362
+
363
+ // Assign arrow position
364
+ if (middlewareData && middlewareData.arrow) {
365
+ const {
366
+ x: arrowX,
367
+ y: arrowY
368
+ } = middlewareData.arrow;
369
+
370
+ /**
371
+ * Clamp arrow X position to a minimum of 24px from the edge of the dropdown.
372
+ * This prevents wide toggles from pushing the arrow to the very edge of the dropdown.
373
+ */
374
+ const toggleRect = this.toggleElement.getBoundingClientRect();
375
+ const contentRect = this.$refs.content.getBoundingClientRect();
376
+ const clampedArrowX = toggleRect.width > contentRect.width ? Math.min(Math.max(arrowX, ARROW_X_MINIMUM), contentRect.width - ARROW_X_MINIMUM) : arrowX;
377
+ Object.assign(this.$refs.dropdownArrow.style, {
378
+ left: arrowX != null ? `${clampedArrowX}px` : '',
379
+ top: arrowY != null ? `${arrowY}px` : '',
380
+ right: '',
381
+ bottom: '',
382
+ [staticSide]: '-4px',
383
+ transform: `rotate(${rotation})`
384
+ });
385
+ }
329
386
  });
330
387
  },
331
388
  stopFloating() {
@@ -435,7 +492,7 @@ var script = {
435
492
  const __vue_script__ = script;
436
493
 
437
494
  /* template */
438
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:"outside",rawName:"v-outside.click.focusin",value:(_vm.close),expression:"close",modifiers:{"click":true,"focusin":true}}],class:[_vm.$options.BASE_DROPDOWN_CLASS, { '!gl-block': _vm.block }]},[_c(_vm.toggleComponent,_vm._g(_vm._b({ref:"toggle",tag:"component",attrs:{"id":_vm.toggleId,"data-testid":"base-dropdown-toggle"},on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.close.apply(null, arguments)}}},'component',_vm.toggleAttributes,false),_vm.toggleListeners),[_vm._t("toggle",function(){return [_c('span',{staticClass:"gl-new-dropdown-button-text",class:{ 'gl-sr-only': _vm.textSrOnly }},[_vm._v("\n "+_vm._s(_vm.toggleText)+"\n ")]),_vm._v(" "),(!_vm.noCaret)?_c('gl-icon',{staticClass:"gl-button-icon gl-new-dropdown-chevron",attrs:{"name":"chevron-down"}}):_vm._e()]})],2),_vm._v(" "),_c('div',{ref:"content",staticClass:"gl-new-dropdown-panel",class:_vm.panelClasses,attrs:{"id":_vm.baseDropdownId,"data-testid":"base-dropdown-menu"},on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.closeAndFocus.apply(null, arguments)}}},[_c('div',{staticClass:"gl-new-dropdown-inner"},[_vm._t("default")],2)])],1)};
495
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:"outside",rawName:"v-outside.click.focusin",value:(_vm.close),expression:"close",modifiers:{"click":true,"focusin":true}}],class:[_vm.$options.BASE_DROPDOWN_CLASS, { '!gl-block': _vm.block }]},[_c(_vm.toggleComponent,_vm._g(_vm._b({ref:"toggle",tag:"component",attrs:{"id":_vm.toggleId,"data-testid":"base-dropdown-toggle"},on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.close.apply(null, arguments)}}},'component',_vm.toggleAttributes,false),_vm.toggleListeners),[_vm._t("toggle",function(){return [_c('span',{staticClass:"gl-new-dropdown-button-text",class:{ 'gl-sr-only': _vm.textSrOnly }},[_vm._v("\n "+_vm._s(_vm.toggleText)+"\n ")]),_vm._v(" "),(!_vm.noCaret)?_c('gl-icon',{staticClass:"gl-button-icon gl-new-dropdown-chevron",attrs:{"name":"chevron-down"}}):_vm._e()]})],2),_vm._v(" "),_c('div',{ref:"content",staticClass:"gl-new-dropdown-panel",class:_vm.panelClasses,attrs:{"id":_vm.baseDropdownId,"data-testid":"base-dropdown-menu"},on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"esc",27,$event.key,["Esc","Escape"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.closeAndFocus.apply(null, arguments)}}},[_c('div',{ref:"dropdownArrow",staticClass:"gl-new-dropdown-arrow"}),_vm._v(" "),_c('div',{staticClass:"gl-new-dropdown-inner"},[_vm._t("default")],2)])],1)};
439
496
  var __vue_staticRenderFns__ = [];
440
497
 
441
498
  /* style */
@@ -1,4 +1,5 @@
1
1
  const FIXED_WIDTH_CLASS = '!gl-w-31';
2
- const DEFAULT_OFFSET = 4;
2
+ const DEFAULT_OFFSET = 8;
3
+ const ARROW_X_MINIMUM = 24;
3
4
 
4
- export { DEFAULT_OFFSET, FIXED_WIDTH_CLASS };
5
+ export { ARROW_X_MINIMUM, DEFAULT_OFFSET, FIXED_WIDTH_CLASS };
@@ -340,7 +340,7 @@ var script = {
340
340
  const __vue_script__ = script;
341
341
 
342
342
  /* template */
343
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-base-dropdown',{ref:"baseDropdown",staticClass:"gl-disclosure-dropdown",attrs:{"aria-labelledby":_vm.toggleAriaLabelledBy,"toggle-id":_vm.toggleId,"toggle-text":_vm.toggleText,"toggle-class":_vm.toggleClass,"text-sr-only":_vm.textSrOnly,"category":_vm.category,"variant":_vm.variant,"size":_vm.size,"icon":_vm.icon,"disabled":_vm.disabled,"loading":_vm.loading,"no-caret":_vm.noCaret,"placement":_vm.placement,"block":_vm.block,"offset":_vm.dropdownOffset,"fluid-width":_vm.fluidWidth,"positioning-strategy":_vm.positioningStrategy},on:_vm._d({},[_vm.$options.events.GL_DROPDOWN_SHOWN,_vm.onShow,_vm.$options.events.GL_DROPDOWN_HIDDEN,_vm.onHide,_vm.$options.events.GL_DROPDOWN_BEFORE_CLOSE,_vm.onBeforeClose,_vm.$options.events.GL_DROPDOWN_FOCUS_CONTENT,_vm.onKeydown]),scopedSlots:_vm._u([(_vm.hasCustomToggle)?{key:"toggle",fn:function(){return [_vm._t("toggle")]},proxy:true}:null],null,true)},[_vm._v(" "),_vm._t("header"),_vm._v(" "),_c(_vm.disclosureTag,{ref:"content",tag:"component",class:_vm.$options.GL_DROPDOWN_CONTENTS_CLASS,attrs:{"id":_vm.disclosureId,"aria-labelledby":_vm.listAriaLabelledBy || _vm.toggleId,"data-testid":"disclosure-content","tabindex":"-1"},on:{"keydown":_vm.onKeydown,"click":_vm.handleAutoClose}},[_vm._t("default",function(){return [_vm._l((_vm.items),function(item,index){return [(_vm.isItem(item))?[_c('gl-disclosure-dropdown-item',{key:_vm.uniqueItemId(),attrs:{"item":item},on:{"action":_vm.handleAction},scopedSlots:_vm._u([('list-item' in _vm.$scopedSlots)?{key:"list-item",fn:function(){return [_vm._t("list-item",null,{"item":item})]},proxy:true}:null],null,true)})]:[_c('gl-disclosure-dropdown-group',{key:item.name,attrs:{"bordered":index !== 0,"group":item},on:{"action":_vm.handleAction},scopedSlots:_vm._u([(_vm.$scopedSlots['group-label'])?{key:"group-label",fn:function(){return [_vm._t("group-label",null,{"group":item})]},proxy:true}:null,(_vm.$scopedSlots['list-item'])?{key:"default",fn:function(){return _vm._l((item.items),function(groupItem){return _c('gl-disclosure-dropdown-item',{key:_vm.uniqueItemId(),attrs:{"item":groupItem},on:{"action":_vm.handleAction},scopedSlots:_vm._u([{key:"list-item",fn:function(){return [_vm._t("list-item",null,{"item":groupItem})]},proxy:true}],null,true)})})},proxy:true}:null],null,true)})]]})]})],2),_vm._v(" "),_vm._t("footer")],2)};
343
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-base-dropdown',{ref:"baseDropdown",staticClass:"gl-disclosure-dropdown",attrs:{"aria-labelledby":_vm.toggleAriaLabelledBy,"arrow-element":_vm.$refs.disclosureArrow,"toggle-id":_vm.toggleId,"toggle-text":_vm.toggleText,"toggle-class":_vm.toggleClass,"text-sr-only":_vm.textSrOnly,"category":_vm.category,"variant":_vm.variant,"size":_vm.size,"icon":_vm.icon,"disabled":_vm.disabled,"loading":_vm.loading,"no-caret":_vm.noCaret,"placement":_vm.placement,"block":_vm.block,"offset":_vm.dropdownOffset,"fluid-width":_vm.fluidWidth,"positioning-strategy":_vm.positioningStrategy},on:_vm._d({},[_vm.$options.events.GL_DROPDOWN_SHOWN,_vm.onShow,_vm.$options.events.GL_DROPDOWN_HIDDEN,_vm.onHide,_vm.$options.events.GL_DROPDOWN_BEFORE_CLOSE,_vm.onBeforeClose,_vm.$options.events.GL_DROPDOWN_FOCUS_CONTENT,_vm.onKeydown]),scopedSlots:_vm._u([(_vm.hasCustomToggle)?{key:"toggle",fn:function(){return [_vm._t("toggle")]},proxy:true}:null],null,true)},[_vm._v(" "),_vm._t("header"),_vm._v(" "),_c(_vm.disclosureTag,{ref:"content",tag:"component",class:_vm.$options.GL_DROPDOWN_CONTENTS_CLASS,attrs:{"id":_vm.disclosureId,"aria-labelledby":_vm.listAriaLabelledBy || _vm.toggleId,"data-testid":"disclosure-content","tabindex":"-1"},on:{"keydown":_vm.onKeydown,"click":_vm.handleAutoClose}},[_vm._t("default",function(){return [_vm._l((_vm.items),function(item,index){return [(_vm.isItem(item))?[_c('gl-disclosure-dropdown-item',{key:_vm.uniqueItemId(),attrs:{"item":item},on:{"action":_vm.handleAction},scopedSlots:_vm._u([('list-item' in _vm.$scopedSlots)?{key:"list-item",fn:function(){return [_vm._t("list-item",null,{"item":item})]},proxy:true}:null],null,true)})]:[_c('gl-disclosure-dropdown-group',{key:item.name,attrs:{"bordered":index !== 0,"group":item},on:{"action":_vm.handleAction},scopedSlots:_vm._u([(_vm.$scopedSlots['group-label'])?{key:"group-label",fn:function(){return [_vm._t("group-label",null,{"group":item})]},proxy:true}:null,(_vm.$scopedSlots['list-item'])?{key:"default",fn:function(){return _vm._l((item.items),function(groupItem){return _c('gl-disclosure-dropdown-item',{key:_vm.uniqueItemId(),attrs:{"item":groupItem},on:{"action":_vm.handleAction},scopedSlots:_vm._u([{key:"list-item",fn:function(){return [_vm._t("list-item",null,{"item":groupItem})]},proxy:true}],null,true)})})},proxy:true}:null],null,true)})]]})]})],2),_vm._v(" "),_vm._t("footer")],2)};
344
344
  var __vue_staticRenderFns__ = [];
345
345
 
346
346
  /* style */