@gitlab/ui 115.0.0 → 115.1.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/bin/migrate_custom_utils_to_tw.bundled.mjs +172489 -139144
- package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +15 -3
- package/dist/components/base/new_dropdowns/constants.js +5 -2
- package/dist/components/dashboards/dashboard_layout/grid_layout/grid_layout.js +286 -0
- package/dist/components/dashboards/mock_data.js +49 -0
- package/dist/index.css +2 -2
- package/dist/index.css.map +1 -1
- package/dist/tailwind.css +1 -1
- package/dist/tailwind.css.map +1 -1
- package/dist/tokens/docs/tokens-tailwind-docs.dark.json +7986 -0
- package/dist/tokens/docs/tokens-tailwind-docs.json +7986 -0
- package/dist/tokens/figma/constants.tokens.json +944 -236
- package/dist/tokens/figma/contextual.tokens.json +2721 -418
- package/dist/tokens/figma/deprecated.tokens.json +664 -8
- package/dist/tokens/figma/semantic.tokens.json +1130 -169
- package/dist/tokens/js/tokens.dark.js +1089 -1047
- package/dist/tokens/js/tokens.js +1078 -1047
- package/dist/tokens/json/tokens.dark.json +9267 -3
- package/dist/tokens/json/tokens.json +9267 -3
- package/dist/tokens/tailwind/tokens.cjs +76 -407
- package/package.json +22 -49
- package/src/components/base/accordion/accordion_item.vue +1 -1
- package/src/components/base/avatar/avatar.vue +1 -1
- package/src/components/base/button/button.vue +1 -1
- package/src/components/base/dropdown/dropdown.vue +3 -1
- package/src/components/base/filtered_search/filtered_search.vue +1 -1
- package/src/components/base/filtered_search/filtered_search_suggestion_list.vue +1 -1
- package/src/components/base/filtered_search/filtered_search_token.vue +1 -1
- package/src/components/base/filtered_search/filtered_search_token_segment.vue +3 -3
- package/src/components/base/form/form_checkbox_tree/models/tree.js +1 -1
- package/src/components/base/form/form_date/form_date.vue +1 -1
- package/src/components/base/form/form_input/form_input.vue +1 -1
- package/src/components/base/form/form_select/form_select.vue +1 -1
- package/src/components/base/modal/modal.vue +2 -2
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +23 -6
- package/src/components/base/new_dropdowns/constants.js +4 -1
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.md +1 -2
- package/src/components/base/new_dropdowns/listbox/listbox.vue +7 -7
- package/src/components/base/pagination/pagination.vue +2 -2
- package/src/components/base/path/path.vue +1 -1
- package/src/components/base/skeleton_loader/skeleton_loader.vue +9 -9
- package/src/components/base/table/table.md +1 -2
- package/src/components/base/table_lite/table_lite.md +1 -1
- package/src/components/base/tabs/tabs/tabs.vue +1 -1
- package/src/components/base/toast/toast.js +2 -2
- package/src/components/base/token_selector/token_container.vue +1 -1
- package/src/components/base/token_selector/token_selector.vue +2 -2
- package/src/components/base/token_selector/token_selector_dropdown.vue +1 -1
- package/src/components/charts/area/area.vue +3 -3
- package/src/components/charts/bar/bar.vue +2 -2
- package/src/components/charts/column/column.vue +2 -2
- package/src/components/charts/discrete_scatter/discrete_scatter.vue +2 -2
- package/src/components/charts/gauge/gauge.vue +1 -1
- package/src/components/charts/heatmap/heatmap.vue +2 -2
- package/src/components/charts/heatmap/index.js +1 -0
- package/src/components/charts/legend/legend.vue +1 -1
- package/src/components/charts/line/line.vue +3 -3
- package/src/components/charts/sparkline/sparkline.vue +1 -1
- package/src/components/charts/stacked_column/stacked_column.vue +1 -1
- package/src/components/dashboards/dashboard_layout/grid_layout/grid_layout.scss +1 -0
- package/src/components/dashboards/dashboard_layout/grid_layout/grid_layout.vue +247 -0
- package/src/components/dashboards/mock_data.js +40 -0
- package/src/components/mixins/tooltip_mixin.js +1 -1
- package/src/components/utilities/intersection_observer/intersection_observer.vue +1 -1
- package/src/components/utilities/intersperse/intersperse.vue +2 -2
- package/src/components/utilities/sprintf/sprintf.md +1 -1
- package/src/components/utilities/sprintf/sprintf.vue +1 -1
- package/src/components/utilities/truncate/truncate.vue +2 -2
- package/src/config.js +1 -1
- package/src/directives/outside/outside.js +4 -4
- package/src/directives/tooltip/tooltip.js +1 -1
- package/src/scss/components.scss +1 -0
- package/src/tokens/build/docs/tokens-tailwind-docs.dark.json +7986 -0
- package/src/tokens/build/docs/tokens-tailwind-docs.json +7986 -0
- package/src/tokens/build/figma/constants.tokens.json +944 -236
- package/src/tokens/build/figma/contextual.tokens.json +2721 -418
- package/src/tokens/build/figma/deprecated.tokens.json +664 -8
- package/src/tokens/build/figma/semantic.tokens.json +1130 -169
- package/src/tokens/build/json/tokens.dark.json +9267 -3
- package/src/tokens/build/json/tokens.json +9267 -3
- package/src/tokens/constant/color.alpha.tokens.json +60 -15
- package/src/tokens/constant/color.tokens.json +844 -211
- package/src/tokens/constant/line_height.tokens.json +40 -10
- package/src/tokens/contextual/alert.tokens.json +119 -19
- package/src/tokens/contextual/avatar.tokens.json +90 -14
- package/src/tokens/contextual/badge.tokens.json +728 -112
- package/src/tokens/contextual/banner.tokens.json +19 -3
- package/src/tokens/contextual/breadcrumb.tokens.json +6 -1
- package/src/tokens/contextual/broadcast.tokens.json +301 -81
- package/src/tokens/contextual/button.tokens.json +793 -120
- package/src/tokens/contextual/chart.tokens.json +42 -7
- package/src/tokens/contextual/datepicker.tokens.json +13 -2
- package/src/tokens/contextual/dropdown.tokens.json +142 -21
- package/src/tokens/contextual/filtered-search.tokens.json +42 -6
- package/src/tokens/contextual/illustration.tokens.json +195 -33
- package/src/tokens/contextual/label.tokens.json +81 -12
- package/src/tokens/contextual/link.tokens.json +26 -4
- package/src/tokens/contextual/progress-bar.tokens.json +35 -5
- package/src/tokens/contextual/skeleton-loader.tokens.json +12 -2
- package/src/tokens/contextual/spinner.tokens.json +24 -4
- package/src/tokens/contextual/table.tokens.json +14 -2
- package/src/tokens/contextual/tabs.tokens.json +7 -1
- package/src/tokens/contextual/toggle.tokens.json +54 -9
- package/src/tokens/contextual/token-selector.tokens.json +7 -1
- package/src/tokens/contextual/token.tokens.json +14 -2
- package/src/tokens/deprecated/deprecated.color.data_viz.tokens.json +165 -0
- package/src/tokens/deprecated/deprecated.color.theme.tokens.json +216 -0
- package/src/tokens/deprecated/deprecated.color.tokens.json +240 -0
- package/src/tokens/deprecated/deprecated.color.transparency.tokens.json +39 -0
- package/src/tokens/semantic/action.tokens.json +500 -75
- package/src/tokens/semantic/background.tokens.json +49 -7
- package/src/tokens/semantic/border.tokens.json +30 -5
- package/src/tokens/semantic/control.tokens.json +129 -20
- package/src/tokens/semantic/feedback.tokens.json +126 -19
- package/src/tokens/semantic/focus-ring.tokens.json +12 -2
- package/src/tokens/semantic/highlight.tokens.json +26 -4
- package/src/tokens/semantic/icon.tokens.json +63 -9
- package/src/tokens/semantic/shadow.tokens.json +6 -1
- package/src/tokens/semantic/status.tokens.json +120 -18
- package/src/tokens/semantic/text.tokens.json +69 -9
- package/src/tokens/tokens_tailwind_table.vue +1 -1
- package/src/utils/charts/config.js +7 -7
- package/src/utils/charts/utils.js +1 -1
- package/src/utils/use_mock_intersection_observer.js +1 -1
- package/src/utils/utils.js +1 -1
- package/tailwind.defaults.js +1 -1
- package/translations.js +2 -2
|
@@ -162,7 +162,7 @@ export default {
|
|
|
162
162
|
filteredDropdownItems() {
|
|
163
163
|
return this.dropdownItems.filter(
|
|
164
164
|
(dropdownItem) =>
|
|
165
|
-
this.selectedTokens.findIndex((token) => token.id === dropdownItem.id) === -1
|
|
165
|
+
this.selectedTokens.findIndex((token) => token.id === dropdownItem.id) === -1,
|
|
166
166
|
);
|
|
167
167
|
},
|
|
168
168
|
dropdownHasNoItems() {
|
|
@@ -341,7 +341,7 @@ export default {
|
|
|
341
341
|
*/
|
|
342
342
|
this.$emit(
|
|
343
343
|
'input',
|
|
344
|
-
this.selectedTokens.filter((selectedToken) => selectedToken.id !== token.id)
|
|
344
|
+
this.selectedTokens.filter((selectedToken) => selectedToken.id !== token.id),
|
|
345
345
|
);
|
|
346
346
|
/**
|
|
347
347
|
* Fired when a token is removed
|
|
@@ -186,7 +186,7 @@ export default {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
return this.$refs.dropdownItems?.find(
|
|
189
|
-
(ref) => ref.$attrs['data-dropdown-item-id'] === dropdownItem.id
|
|
189
|
+
(ref) => ref.$attrs['data-dropdown-item-id'] === dropdownItem.id,
|
|
190
190
|
);
|
|
191
191
|
},
|
|
192
192
|
dropdownItemIdAttribute(dropdownItem) {
|
|
@@ -173,7 +173,7 @@ export default {
|
|
|
173
173
|
},
|
|
174
174
|
lineStyle,
|
|
175
175
|
series,
|
|
176
|
-
getThresholdConfig(this.thresholds)
|
|
176
|
+
getThresholdConfig(this.thresholds),
|
|
177
177
|
);
|
|
178
178
|
});
|
|
179
179
|
// if annotation series exists, append it
|
|
@@ -214,13 +214,13 @@ export default {
|
|
|
214
214
|
areaChartOptions,
|
|
215
215
|
this.formatTooltipText ? deprecatedTooltipFormatterOptions : {},
|
|
216
216
|
this.option,
|
|
217
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
217
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
218
218
|
);
|
|
219
219
|
// All chart options can be merged but series
|
|
220
220
|
// needs to be handled specially.
|
|
221
221
|
return mergeSeriesToOptions(
|
|
222
222
|
mergeAnnotationAxisToOptions(mergedOptions, this.hasAnnotations),
|
|
223
|
-
this.series
|
|
223
|
+
this.series,
|
|
224
224
|
);
|
|
225
225
|
},
|
|
226
226
|
/**
|
|
@@ -157,7 +157,7 @@ export default {
|
|
|
157
157
|
},
|
|
158
158
|
},
|
|
159
159
|
this.option,
|
|
160
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
160
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
161
161
|
);
|
|
162
162
|
// All chart options can be merged but series
|
|
163
163
|
// needs to be handled specially
|
|
@@ -207,7 +207,7 @@ export default {
|
|
|
207
207
|
{
|
|
208
208
|
yLabels: [],
|
|
209
209
|
tooltipContent: {},
|
|
210
|
-
}
|
|
210
|
+
},
|
|
211
211
|
);
|
|
212
212
|
|
|
213
213
|
return { yLabels, tooltipContent };
|
|
@@ -114,7 +114,7 @@ export default {
|
|
|
114
114
|
return type === CHART_TYPE_LINE
|
|
115
115
|
? generateLineSeries({ color, name, data, yAxisIndex: 1 })
|
|
116
116
|
: generateBarSeries({ color, name, data, yAxisIndex: 1, stack });
|
|
117
|
-
}
|
|
117
|
+
},
|
|
118
118
|
);
|
|
119
119
|
},
|
|
120
120
|
series() {
|
|
@@ -158,7 +158,7 @@ export default {
|
|
|
158
158
|
: yAxisPrimary,
|
|
159
159
|
},
|
|
160
160
|
this.option,
|
|
161
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
161
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
162
162
|
);
|
|
163
163
|
// All chart options can be merged but series
|
|
164
164
|
// needs to be handled specially
|
|
@@ -90,7 +90,7 @@ export default {
|
|
|
90
90
|
color: getColor('itemStyle'),
|
|
91
91
|
},
|
|
92
92
|
},
|
|
93
|
-
series
|
|
93
|
+
series,
|
|
94
94
|
);
|
|
95
95
|
});
|
|
96
96
|
},
|
|
@@ -125,7 +125,7 @@ export default {
|
|
|
125
125
|
},
|
|
126
126
|
},
|
|
127
127
|
this.option,
|
|
128
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
128
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
129
129
|
);
|
|
130
130
|
// All chart options can be merged but series
|
|
131
131
|
// needs to be handled specially
|
|
@@ -36,7 +36,7 @@ function getRange(series) {
|
|
|
36
36
|
if (value > acc.max) acc.max = value;
|
|
37
37
|
return acc;
|
|
38
38
|
},
|
|
39
|
-
{ min: 0, max: 0 }
|
|
39
|
+
{ min: 0, max: 0 },
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -197,7 +197,7 @@ export default {
|
|
|
197
197
|
},
|
|
198
198
|
},
|
|
199
199
|
},
|
|
200
|
-
this.option
|
|
200
|
+
this.option,
|
|
201
201
|
);
|
|
202
202
|
},
|
|
203
203
|
legendStyle() {
|
|
@@ -149,7 +149,7 @@ export default {
|
|
|
149
149
|
},
|
|
150
150
|
suppressLastActiveSeriesLabelToggle({ selected }) {
|
|
151
151
|
const selectedSeriesLabels = Object.entries(selected).filter(([, isSelected]) =>
|
|
152
|
-
Boolean(isSelected)
|
|
152
|
+
Boolean(isSelected),
|
|
153
153
|
);
|
|
154
154
|
|
|
155
155
|
this.lastActiveSeriesLabel = null;
|
|
@@ -167,7 +167,7 @@ export default {
|
|
|
167
167
|
symbolSize,
|
|
168
168
|
lineStyle,
|
|
169
169
|
series,
|
|
170
|
-
getThresholdConfig(this.thresholds)
|
|
170
|
+
getThresholdConfig(this.thresholds),
|
|
171
171
|
);
|
|
172
172
|
});
|
|
173
173
|
// if annotation series exists, append it
|
|
@@ -212,13 +212,13 @@ export default {
|
|
|
212
212
|
lineChartOptions,
|
|
213
213
|
this.formatTooltipText ? deprecatedTooltipFormatterOptions : {},
|
|
214
214
|
this.option,
|
|
215
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
215
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
216
216
|
);
|
|
217
217
|
// All chart options can be merged but series
|
|
218
218
|
// needs to be handled specially
|
|
219
219
|
return mergeSeriesToOptions(
|
|
220
220
|
mergeAnnotationAxisToOptions(mergedOptions, this.hasAnnotations),
|
|
221
|
-
this.series
|
|
221
|
+
this.series,
|
|
222
222
|
);
|
|
223
223
|
},
|
|
224
224
|
/**
|
|
@@ -263,7 +263,7 @@ export default {
|
|
|
263
263
|
stackedColumnChartOptions,
|
|
264
264
|
this.formatTooltipText ? deprecatedTooltipFormatterOptions : {},
|
|
265
265
|
this.option,
|
|
266
|
-
dataZoomAdjustments(this.option.dataZoom)
|
|
266
|
+
dataZoomAdjustments(this.option.dataZoom),
|
|
267
267
|
);
|
|
268
268
|
// All chart options can be merged but series
|
|
269
269
|
// needs to be handled specially
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import 'gridstack/dist/gridstack';
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { GridStack } from 'gridstack';
|
|
3
|
+
import pickBy from 'lodash/pickBy';
|
|
4
|
+
import { breakpoints } from '../../../../utils/breakpoints';
|
|
5
|
+
|
|
6
|
+
const CURSOR_GRABBING_CLASS = '!gl-cursor-grabbing';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: 'GlGridLayout',
|
|
10
|
+
props: {
|
|
11
|
+
value: {
|
|
12
|
+
type: Object,
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
isStatic: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
required: false,
|
|
18
|
+
default: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
grid: undefined,
|
|
24
|
+
gridPanels: [],
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
computed: {
|
|
28
|
+
gridConfig() {
|
|
29
|
+
return this.value.panels.map((panel) => {
|
|
30
|
+
const { gridAttributes, ...otherProps } = panel;
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
...this.getPanelGridItemConfig(panel),
|
|
34
|
+
props: otherProps,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
watch: {
|
|
40
|
+
isStatic(value) {
|
|
41
|
+
this.grid?.setStatic(value);
|
|
42
|
+
},
|
|
43
|
+
gridConfig: {
|
|
44
|
+
handler(config) {
|
|
45
|
+
this.grid?.load(config);
|
|
46
|
+
},
|
|
47
|
+
deep: true,
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Data flow:
|
|
51
|
+
* 1. Initial: mounted → initGridStack() → grid.load(gridConfig) →
|
|
52
|
+
* grid.getGridItems() → initGridPanelSlots → gridPanels populated with DOM references
|
|
53
|
+
* 2. Updates: value.panels changes → two parallel paths:
|
|
54
|
+
* a. gridConfig changes → grid.load() updates grid layout (but not gridPanels)
|
|
55
|
+
* b. this watcher updates gridPanels with new panel properties
|
|
56
|
+
*/
|
|
57
|
+
'value.panels': {
|
|
58
|
+
handler(newPanels) {
|
|
59
|
+
if (this.gridPanels.length === 0) return;
|
|
60
|
+
|
|
61
|
+
// Only update panels that have changed to improve performance
|
|
62
|
+
newPanels.forEach((updatedPanel) => {
|
|
63
|
+
const panel = this.gridPanels.find((p) => p.id === updatedPanel.id);
|
|
64
|
+
|
|
65
|
+
if (panel) {
|
|
66
|
+
// Exclude `gridAttributes` from being included in the panel props as it's not a valid prop for the panel component
|
|
67
|
+
const panelPropsWithoutGridAttributes = pickBy(
|
|
68
|
+
updatedPanel,
|
|
69
|
+
(_, k) => k !== 'gridAttributes',
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
panel.props = { ...panelPropsWithoutGridAttributes };
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
deep: true,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
mounted() {
|
|
80
|
+
this.initGridStack();
|
|
81
|
+
},
|
|
82
|
+
beforeDestroy() {
|
|
83
|
+
const removeDom = Boolean(this.$el.parentElement);
|
|
84
|
+
this.grid?.destroy(removeDom);
|
|
85
|
+
},
|
|
86
|
+
methods: {
|
|
87
|
+
// TODO: Refactor this to use render methods once Vue 3 migration is complete
|
|
88
|
+
// https://gitlab.com/gitlab-org/gitlab/-/issues/549095
|
|
89
|
+
async mountGridComponents(panels, options = { scrollIntoView: false }) {
|
|
90
|
+
// Ensure new panels are always rendered first
|
|
91
|
+
await this.$nextTick();
|
|
92
|
+
|
|
93
|
+
panels.forEach((panel) => {
|
|
94
|
+
const wrapper = this.$refs.panelWrappers?.find((w) => w.id === panel.id);
|
|
95
|
+
const widgetContentEl = panel.el.querySelector('.grid-stack-item-content');
|
|
96
|
+
|
|
97
|
+
if (wrapper && widgetContentEl) {
|
|
98
|
+
widgetContentEl.appendChild(wrapper);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (options.scrollIntoView) {
|
|
103
|
+
const mostRecent = panels[panels.length - 1];
|
|
104
|
+
mostRecent.el.scrollIntoView({ behavior: 'smooth' });
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
getGridItemForElement(el) {
|
|
108
|
+
return this.gridConfig.find((item) => item.id === el.getAttribute('gs-id'));
|
|
109
|
+
},
|
|
110
|
+
initGridPanelSlots(gridElements) {
|
|
111
|
+
if (!gridElements) return;
|
|
112
|
+
|
|
113
|
+
this.gridPanels = gridElements.map((el) => ({
|
|
114
|
+
...this.getGridItemForElement(el),
|
|
115
|
+
el,
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
this.mountGridComponents(this.gridPanels);
|
|
119
|
+
},
|
|
120
|
+
initGridStack() {
|
|
121
|
+
// See https://github.com/gridstack/gridstack.js/tree/master/doc#grid-options
|
|
122
|
+
this.grid = GridStack.init(
|
|
123
|
+
{
|
|
124
|
+
// Uniform gap between panels
|
|
125
|
+
margin: '8px',
|
|
126
|
+
// CSS Selector for finding the drag handle element
|
|
127
|
+
handle: '.grid-stack-item-handle',
|
|
128
|
+
/* Magic number 125px:
|
|
129
|
+
* After allowing for padding, and the panel title row, this leaves us with minimum 48px height for the cell content.
|
|
130
|
+
* This means text/content with our spacing scale can fit up to 49px without scrolling.
|
|
131
|
+
*/
|
|
132
|
+
cellHeight: '125px',
|
|
133
|
+
// Setting 1 in minRow prevents the grid collapsing when all panels are removed
|
|
134
|
+
minRow: 1,
|
|
135
|
+
// Define the number of columns for anything below a set width, defaults to fill the available space
|
|
136
|
+
columnOpts: { breakpoints: [{ w: breakpoints.md, c: 1 }] },
|
|
137
|
+
alwaysShowResizeHandle: true,
|
|
138
|
+
animate: true,
|
|
139
|
+
float: true,
|
|
140
|
+
// Toggles user-customization of grid layout
|
|
141
|
+
staticGrid: this.isStatic,
|
|
142
|
+
},
|
|
143
|
+
this.$refs.grid,
|
|
144
|
+
).load(this.gridConfig);
|
|
145
|
+
|
|
146
|
+
// Sync Vue components array with gridstack items
|
|
147
|
+
this.initGridPanelSlots(this.grid.getGridItems());
|
|
148
|
+
|
|
149
|
+
this.grid.on('dragstart', () => {
|
|
150
|
+
this.$el.classList.add(CURSOR_GRABBING_CLASS);
|
|
151
|
+
});
|
|
152
|
+
this.grid.on('dragstop', () => {
|
|
153
|
+
this.$el.classList.remove(CURSOR_GRABBING_CLASS);
|
|
154
|
+
});
|
|
155
|
+
this.grid.on('change', (_, items) => {
|
|
156
|
+
if (!items) return;
|
|
157
|
+
|
|
158
|
+
this.emitLayoutChanges(items);
|
|
159
|
+
});
|
|
160
|
+
this.grid.on('added', (_, items) => {
|
|
161
|
+
this.addGridPanels(items);
|
|
162
|
+
});
|
|
163
|
+
this.grid.on('removed', (_, items) => {
|
|
164
|
+
this.removeGridPanels(items);
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
getPanelGridItemConfig({
|
|
168
|
+
gridAttributes: { xPos, yPos, width, height, minHeight, minWidth, maxHeight, maxWidth },
|
|
169
|
+
id,
|
|
170
|
+
}) {
|
|
171
|
+
const filterUndefinedValues = (obj) => pickBy(obj, (value) => value !== undefined);
|
|
172
|
+
|
|
173
|
+
// GridStack renders undefined layout values so we need to filter them out.
|
|
174
|
+
return filterUndefinedValues({
|
|
175
|
+
x: xPos,
|
|
176
|
+
y: yPos,
|
|
177
|
+
w: width,
|
|
178
|
+
h: height,
|
|
179
|
+
minH: minHeight,
|
|
180
|
+
minW: minWidth,
|
|
181
|
+
maxH: maxHeight,
|
|
182
|
+
maxW: maxWidth,
|
|
183
|
+
id,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
convertToGridAttributes(gridStackItem) {
|
|
187
|
+
return {
|
|
188
|
+
yPos: gridStackItem.y,
|
|
189
|
+
xPos: gridStackItem.x,
|
|
190
|
+
width: gridStackItem.w,
|
|
191
|
+
height: gridStackItem.h,
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
removeGridPanels(items) {
|
|
195
|
+
items.forEach((item) => {
|
|
196
|
+
const index = this.gridPanels.findIndex((c) => c.id === item.id);
|
|
197
|
+
this.gridPanels.splice(index, 1);
|
|
198
|
+
// Finally, remove the gridstack element
|
|
199
|
+
item.el.remove();
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
addGridPanels(items) {
|
|
203
|
+
const newPanels = items.map(({ grid, ...rest }) => ({ ...rest }));
|
|
204
|
+
this.gridPanels.push(...newPanels);
|
|
205
|
+
|
|
206
|
+
this.mountGridComponents(newPanels, { scrollIntoView: true });
|
|
207
|
+
},
|
|
208
|
+
emitLayoutChanges(items) {
|
|
209
|
+
/**
|
|
210
|
+
* Uses JSON parse and stringify to remove object references.
|
|
211
|
+
* Lodash's `cloneDeep` retains circular references.
|
|
212
|
+
* See https://github.com/lodash/lodash/issues/4710#issuecomment-606892867 for details on cloneDeep circular references
|
|
213
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm for the underlying mechanism used by Lodash
|
|
214
|
+
*/
|
|
215
|
+
const newValue = JSON.parse(JSON.stringify(this.value));
|
|
216
|
+
|
|
217
|
+
items.forEach((item) => {
|
|
218
|
+
const panel = newValue.panels.find((p) => p.id === item.id);
|
|
219
|
+
|
|
220
|
+
if (!panel) return;
|
|
221
|
+
|
|
222
|
+
panel.gridAttributes = {
|
|
223
|
+
...panel.gridAttributes,
|
|
224
|
+
...this.convertToGridAttributes(item),
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
this.$emit('input', newValue);
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
</script>
|
|
232
|
+
|
|
233
|
+
<template>
|
|
234
|
+
<div ref="grid" class="grid-stack" data-testid="gridstack-grid">
|
|
235
|
+
<div
|
|
236
|
+
v-for="panel in gridPanels"
|
|
237
|
+
:id="panel.id"
|
|
238
|
+
ref="panelWrappers"
|
|
239
|
+
:key="panel.id"
|
|
240
|
+
class="gl-h-full"
|
|
241
|
+
:class="{ 'gl-cursor-grab': !isStatic }"
|
|
242
|
+
data-testid="gridstack-panel"
|
|
243
|
+
>
|
|
244
|
+
<slot name="panel" v-bind="{ panel: panel.props }"></slot>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import uniqueId from 'lodash/uniqueId';
|
|
2
|
+
|
|
3
|
+
export const getUniquePanelId = () => uniqueId('panel-');
|
|
4
|
+
|
|
5
|
+
export const dashboard = {
|
|
6
|
+
id: 'analytics_overview',
|
|
7
|
+
slug: 'analytics_overview',
|
|
8
|
+
title: 'Analytics Overview',
|
|
9
|
+
description: 'This is a dashboard',
|
|
10
|
+
userDefined: true,
|
|
11
|
+
panels: [
|
|
12
|
+
{
|
|
13
|
+
title: 'Test A',
|
|
14
|
+
gridAttributes: { width: 3, height: 3, xPos: 0, yPos: 0 },
|
|
15
|
+
id: getUniquePanelId(),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
title: 'Test B',
|
|
19
|
+
gridAttributes: { width: 2, height: 4, xPos: 1, yPos: 1, minHeight: 2, minWidth: 2 },
|
|
20
|
+
id: getUniquePanelId(),
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
status: null,
|
|
24
|
+
errors: null,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const mockPanel = {
|
|
28
|
+
title: 'Test A',
|
|
29
|
+
gridAttributes: {
|
|
30
|
+
width: 1,
|
|
31
|
+
height: 2,
|
|
32
|
+
xPos: 0,
|
|
33
|
+
yPos: 3,
|
|
34
|
+
minWidth: 1,
|
|
35
|
+
minHeight: 2,
|
|
36
|
+
maxWidth: 1,
|
|
37
|
+
maxHeight: 2,
|
|
38
|
+
},
|
|
39
|
+
id: getUniquePanelId(),
|
|
40
|
+
};
|
|
@@ -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].$emit(event)),
|
|
16
16
|
);
|
|
17
17
|
},
|
|
18
18
|
beforeDestroy() {
|
|
@@ -8,7 +8,7 @@ import { intersperse, insert } from '../../../utils/data_utils';
|
|
|
8
8
|
import { isVnodeEmpty } from '../../../utils/is_slot_empty';
|
|
9
9
|
|
|
10
10
|
const filterEmptyNodesVue2 = filter(
|
|
11
|
-
(vNode) => typeof vNode.tag === 'string' || vNode.text.trim() !== ''
|
|
11
|
+
(vNode) => typeof vNode.tag === 'string' || vNode.text.trim() !== '',
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
const { Fragment } = Vue;
|
|
@@ -67,7 +67,7 @@ export default {
|
|
|
67
67
|
const filterAndSeparate = compose(
|
|
68
68
|
addLastSeparator(lastSeparator),
|
|
69
69
|
intersperse(separator),
|
|
70
|
-
filterEmptyNodes
|
|
70
|
+
filterEmptyNodes,
|
|
71
71
|
);
|
|
72
72
|
|
|
73
73
|
return createElement('span', data, filterAndSeparate(slots().default));
|
|
@@ -140,7 +140,7 @@ The example above renders to this HTML:
|
|
|
140
140
|
|
|
141
141
|
`GlSprintf` does not handle white space in scoped slots specially; it is passed
|
|
142
142
|
through and rendered just like regular text. This means that white space in the
|
|
143
|
-
scoped slot templates
|
|
143
|
+
scoped slot templates _themselves_, including newlines and indentation, are
|
|
144
144
|
passed through untouched (assuming the template compiler you're using doesn't
|
|
145
145
|
trim text nodes at compile time; `vue-template-compiler` preserves white space
|
|
146
146
|
by default, for instance).
|
|
@@ -69,7 +69,7 @@ export default {
|
|
|
69
69
|
validator: (value) =>
|
|
70
70
|
Object.values(value).every(
|
|
71
71
|
// eslint-disable-next-line unicorn/no-array-callback-reference
|
|
72
|
-
(tagPair) => Array.isArray(tagPair) && tagPair.length === 2 && tagPair.every(isString)
|
|
72
|
+
(tagPair) => Array.isArray(tagPair) && tagPair.length === 2 && tagPair.every(isString),
|
|
73
73
|
),
|
|
74
74
|
},
|
|
75
75
|
},
|
|
@@ -55,10 +55,10 @@ export default {
|
|
|
55
55
|
const collapsibleWhitespaceChar = /^[ \n\t\r\f]$/;
|
|
56
56
|
const { text, middleIndex } = this;
|
|
57
57
|
const lastCharOfFirstIsCollapsibleWhitespace = collapsibleWhitespaceChar.test(
|
|
58
|
-
text.charAt(middleIndex - 1)
|
|
58
|
+
text.charAt(middleIndex - 1),
|
|
59
59
|
);
|
|
60
60
|
const firstCharOfLastIsCollapsibleWhitespace = collapsibleWhitespaceChar.test(
|
|
61
|
-
text.charAt(middleIndex)
|
|
61
|
+
text.charAt(middleIndex),
|
|
62
62
|
);
|
|
63
63
|
|
|
64
64
|
return lastCharOfFirstIsCollapsibleWhitespace && !firstCharOfLastIsCollapsibleWhitespace;
|
package/src/config.js
CHANGED
|
@@ -81,7 +81,7 @@ const setConfigs = ({ translations, firstDayOfWeek } = {}) => {
|
|
|
81
81
|
if (undefinedTranslationKeys.length) {
|
|
82
82
|
/* eslint-disable no-console */
|
|
83
83
|
console.warn(
|
|
84
|
-
'[@gitlab/ui] The following translations have not been given, so will fall back to their default US English strings:'
|
|
84
|
+
'[@gitlab/ui] The following translations have not been given, so will fall back to their default US English strings:',
|
|
85
85
|
);
|
|
86
86
|
console.table(undefinedTranslationKeys);
|
|
87
87
|
/* eslint-enable no-console */
|
|
@@ -94,19 +94,19 @@ function parseBinding({ arg, value, modifiers }) {
|
|
|
94
94
|
|
|
95
95
|
if (typeof arg !== 'undefined') {
|
|
96
96
|
throw new Error(
|
|
97
|
-
`[GlOutsideDirective] Arguments are not supported. Consider using modifiers instead
|
|
97
|
+
`[GlOutsideDirective] Arguments are not supported. Consider using modifiers instead.`,
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const unsupportedModifiers = modifiersList.filter(
|
|
102
|
-
(modifier) => !supportedEventTypes.includes(modifier)
|
|
102
|
+
(modifier) => !supportedEventTypes.includes(modifier),
|
|
103
103
|
);
|
|
104
104
|
|
|
105
105
|
if (unsupportedModifiers.length > 0) {
|
|
106
106
|
throw new Error(
|
|
107
107
|
`[GlOutsideDirective] Cannot bind ${unsupportedModifiers.join(', ')} events; supported event types are: ${supportedEventTypes.join(
|
|
108
|
-
', '
|
|
109
|
-
)}
|
|
108
|
+
', ',
|
|
109
|
+
)}`,
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
112
|
}
|
package/src/scss/components.scss
CHANGED
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
@import '../components/charts/single_stat/single_stat';
|
|
73
73
|
@import '../components/charts/shared/tooltip/tooltip';
|
|
74
74
|
@import '../components/charts/shared/tooltip/tooltip_default_format/tooltip_default_format';
|
|
75
|
+
@import '../components/dashboards/dashboard_layout/grid_layout/grid_layout';
|
|
75
76
|
@import '../components/utilities/truncate/truncate';
|
|
76
77
|
@import '../components/utilities/truncate_text/truncate_text';
|
|
77
78
|
@import '../components/base/new_dropdowns/dropdown';
|