@carbon/vue 1.0.2 → 1.1.3

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/README.md +156 -211
  3. package/dist/carbon-vue.common.js +10567 -12509
  4. package/dist/carbon-vue.common.js.map +1 -1
  5. package/dist/carbon-vue.umd.js +10567 -12509
  6. package/dist/carbon-vue.umd.js.map +1 -1
  7. package/dist/carbon-vue.umd.min.js +31 -7
  8. package/dist/carbon-vue.umd.min.js.map +1 -1
  9. package/dist/demo.html +2 -2
  10. package/package.json +17 -157
  11. package/src/components/cv-accordion/_cv-accordion-item-skeleton.vue +3 -13
  12. package/src/components/cv-accordion/cv-accordion-item.vue +2 -9
  13. package/src/components/cv-breadcrumb/cv-breadcrumb-item.vue +3 -1
  14. package/src/components/cv-button/cv-button-skeleton.vue +1 -6
  15. package/src/components/cv-button/cv-button.vue +15 -37
  16. package/src/components/cv-checkbox/cv-checkbox.vue +42 -26
  17. package/src/components/cv-code-snippet/_cv-code-snippet-multiline.vue +13 -23
  18. package/src/components/cv-code-snippet/_cv-code-snippet-oneline.vue +11 -5
  19. package/src/components/cv-code-snippet/_cv-feedback-button.vue +1 -3
  20. package/src/components/cv-code-snippet/cv-code-snippet.vue +2 -7
  21. package/src/components/cv-content-switcher/cv-content-switcher-button.vue +20 -4
  22. package/src/components/cv-content-switcher/cv-content-switcher.vue +3 -10
  23. package/src/components/cv-data-table/cv-data-table-row.vue +15 -11
  24. package/src/components/cv-data-table/cv-data-table.vue +30 -28
  25. package/src/components/cv-date-picker/cv-date-picker.vue +53 -47
  26. package/src/components/cv-dropdown/_cv-dropdown-inner-c10.vue +226 -0
  27. package/src/components/cv-dropdown/_cv-dropdown-inner.vue +201 -0
  28. package/src/components/cv-dropdown/cv-dropdown-item.vue +6 -8
  29. package/src/components/cv-dropdown/cv-dropdown-skeleton.vue +1 -5
  30. package/src/components/cv-dropdown/cv-dropdown.vue +22 -169
  31. package/src/components/cv-file-uploader/cv-file-uploader.vue +44 -22
  32. package/src/components/cv-inline-loading/cv-inline-loading.vue +37 -0
  33. package/src/components/cv-inline-notification/cv-inline-notification.vue +22 -19
  34. package/src/components/cv-link/cv-link.vue +1 -6
  35. package/src/components/cv-loading/cv-loading.vue +13 -4
  36. package/src/components/cv-modal/cv-modal.vue +17 -15
  37. package/src/components/cv-number-input/cv-number-input.vue +20 -32
  38. package/src/components/cv-overflow-menu/cv-overflow-menu.vue +13 -19
  39. package/src/components/cv-pagination/cv-pagination.vue +61 -42
  40. package/src/components/cv-progress/_cv-progress-step-complete.vue +3 -12
  41. package/src/components/cv-progress/cv-progress-step.vue +97 -0
  42. package/src/components/cv-progress/cv-progress.vue +68 -13
  43. package/src/components/cv-radio-button/cv-radio-button.vue +1 -0
  44. package/src/components/cv-search/cv-search.vue +144 -15
  45. package/src/components/cv-select/cv-select-option.vue +3 -1
  46. package/src/components/cv-select/cv-select.vue +147 -15
  47. package/src/components/cv-slider/cv-slider.vue +14 -43
  48. package/src/components/cv-structured-list/_cv-structured-list-item-selectable.vue +18 -6
  49. package/src/components/cv-structured-list/cv-structured-list-item.vue +1 -3
  50. package/src/components/cv-structured-list/cv-structured-list.vue +10 -2
  51. package/src/components/cv-tabs/cv-tabs.vue +31 -15
  52. package/src/components/cv-tag/cv-tag.vue +23 -13
  53. package/src/components/cv-text-area/cv-text-area.vue +23 -10
  54. package/src/components/cv-text-input/cv-text-input.vue +80 -10
  55. package/src/components/cv-tile/_cv-tile-clickable.vue +1 -5
  56. package/src/components/cv-tile/_cv-tile-expandable.vue +11 -15
  57. package/src/components/cv-tile/_cv-tile-selectable.vue +14 -3
  58. package/src/components/cv-tile/cv-tile.vue +1 -4
  59. package/src/components/cv-time-picker/cv-time-picker.vue +27 -29
  60. package/src/components/cv-toast-notification/cv-toast-notification.vue +32 -6
  61. package/src/components/cv-toggle/cv-toggle.vue +3 -7
  62. package/src/components/cv-toolbar/cv-toolbar-option.vue +1 -4
  63. package/src/components/cv-toolbar/cv-toolbar-search.vue +5 -0
  64. package/src/components/cv-tooltip/cv-definition-tooltip.vue +1 -3
  65. package/src/components/cv-tooltip/cv-interactive-tooltip.vue +13 -28
  66. package/src/components/cv-tooltip/cv-tooltip.vue +11 -15
  67. package/src/components/cv-wrapper/_cv-wrapper.js +33 -0
  68. package/src/index-components-vue.js +1 -3
  69. package/src/index.js +1 -5
  70. package/src/{_internal/_feature-flags.js → internal/feature-flags.js} +30 -2
  71. package/dist/carbon-data-viz-vue.common.js +0 -23907
  72. package/dist/carbon-data-viz-vue.common.js.map +0 -1
  73. package/dist/carbon-data-viz-vue.css +0 -1
  74. package/dist/carbon-data-viz-vue.umd.js +0 -23917
  75. package/dist/carbon-data-viz-vue.umd.js.map +0 -1
  76. package/dist/carbon-data-viz-vue.umd.min.js +0 -2
  77. package/dist/carbon-data-viz-vue.umd.min.js.map +0 -1
  78. package/src/assets/styles/_carbon-helpers.scss +0 -8
  79. package/src/components/cv-checkbox/_cv-checkbox-inner-form-item.vue +0 -19
  80. package/src/components/cv-checkbox/_cv-checkbox-inner.vue +0 -59
  81. package/src/components/cv-progress/_cv-progress-step.vue +0 -51
  82. package/src/components/cv-search/_cv-search-inner-form-item.vue +0 -22
  83. package/src/components/cv-search/_cv-search-inner.vue +0 -162
  84. package/src/components/cv-select/_cv-select-inner-form-item.vue +0 -22
  85. package/src/components/cv-select/_cv-select-inner.vue +0 -78
  86. package/src/data-viz/cv-bar-graph/cv-bar-graph.vue +0 -495
  87. package/src/data-viz/cv-gauge/cv-gauge.vue +0 -196
  88. package/src/data-viz/cv-pie-chart/cv-pie-chart.vue +0 -194
  89. package/src/index-data-viz.js +0 -29
@@ -24,12 +24,10 @@ const toggleContent = (selector, on) => {
24
24
  export default {
25
25
  name: 'CvContentSwitcher',
26
26
  created() {
27
- // add these on created otherwise cv:mounted is too early.
27
+ // add these on created otherwise cv:mounted is too late.
28
28
  this.$on('cv:open', srcComponent => this.onCvOpen(srcComponent));
29
29
  this.$on('cv:mounted', srcComponent => this.onCvMount(srcComponent));
30
- this.$on('cv:beforeDestroy', srcComponent =>
31
- this.onCvBeforeDestroy(srcComponent)
32
- );
30
+ this.$on('cv:beforeDestroy', srcComponent => this.onCvBeforeDestroy(srcComponent));
33
31
  },
34
32
  data() {
35
33
  return {
@@ -92,12 +90,7 @@ export default {
92
90
  }
93
91
  },
94
92
  onCvOpen(srcComponent) {
95
- this.$emit(
96
- 'selected',
97
- srcComponent.ownerId
98
- ? srcComponent.ownerId
99
- : srcComponent.contentSelector
100
- );
93
+ this.$emit('selected', srcComponent.ownerId ? srcComponent.ownerId : srcComponent.contentSelector);
101
94
  this.processState(srcComponent, true);
102
95
  },
103
96
  },
@@ -1,13 +1,7 @@
1
1
  <template>
2
2
  <tr>
3
3
  <td v-if="hasBatchActions">
4
- <cv-checkbox
5
- :form-item="false"
6
- :value="value"
7
- v-model="dataChecked"
8
- @change="onChange"
9
- ref="rowChecked"
10
- />
4
+ <cv-checkbox :form-item="false" :value="value" v-model="dataChecked" @change="onChange" ref="rowChecked" />
11
5
  </td>
12
6
  <slot />
13
7
  <td v-if="hasOverflowMenu">
@@ -30,8 +24,15 @@
30
24
  </template>
31
25
 
32
26
  <script>
27
+ import CvCheckbox from '../cv-checkbox/cv-checkbox';
28
+ import CvOverflowMenu from '../cv-overflow-menu/cv-overflow-menu';
29
+ import CvOverflowMenuItem from '../cv-overflow-menu/cv-overflow-menu-item';
30
+ import uidMixin from '../../mixins/uid-mixin';
31
+
33
32
  export default {
34
33
  name: 'CvDataTableRow',
34
+ components: { CvCheckbox, CvOverflowMenu, CvOverflowMenuItem },
35
+ mixins: [uidMixin],
35
36
  props: {
36
37
  checked: Boolean,
37
38
  overflowMenu: Array,
@@ -51,15 +52,18 @@ export default {
51
52
  dataChecked: this.checked,
52
53
  };
53
54
  },
55
+ mounted() {
56
+ this.$parent.$emit('cv:mounted', this);
57
+ },
58
+ beforeDestroy() {
59
+ this.$parent.$emit('cv:beforeDestroy', this);
60
+ },
54
61
  computed: {
55
62
  isCvDataTableRow() {
56
63
  return true;
57
64
  },
58
65
  hasOverflowMenu() {
59
- return (
60
- (this.overflowMenu && this.overflowMenu.length) ||
61
- this.$slots['overflow-menu']
62
- );
66
+ return (this.overflowMenu && this.overflowMenu.length) || this.$slots['overflow-menu'];
63
67
  },
64
68
  hasBatchActions() {
65
69
  return this.$parent.hasBatchActions;
@@ -19,9 +19,7 @@
19
19
  <span data-items-selected>{{ dataRowsSelected.length }}</span>
20
20
  items selected
21
21
  </p>
22
- <button class="bx--batch-summary__cancel" @click="deselect">
23
- Cancel
24
- </button>
22
+ <button class="bx--batch-summary__cancel" @click="deselect">Cancel</button>
25
23
  </div>
26
24
  </div>
27
25
 
@@ -70,7 +68,7 @@
70
68
  v-for="(row, rowIndex) in data"
71
69
  :key="`row:${rowIndex}`"
72
70
  :value="`${rowIndex}`"
73
- ref="dataRowsSelected"
71
+ ref="dataRows"
74
72
  :overflow-menu="overflowMenu"
75
73
  >
76
74
  <cv-data-table-cell
@@ -103,6 +101,9 @@
103
101
  import CvDataTableHeadnig from './_cv-data-table-heading';
104
102
  import CvDataTableRow from './cv-data-table-row';
105
103
  import CvDataTableCell from './cv-data-table-cell';
104
+ import CvSearch from '../cv-search/cv-search';
105
+ import CvCheckbox from '../cv-checkbox/cv-checkbox';
106
+ import CvPagination from '../cv-pagination/cv-pagination';
106
107
 
107
108
  const rows = children => children.filter(child => child.isCvDataTableRow);
108
109
 
@@ -112,6 +113,9 @@ export default {
112
113
  CvDataTableHeadnig,
113
114
  CvDataTableRow,
114
115
  CvDataTableCell,
116
+ CvSearch,
117
+ CvCheckbox,
118
+ CvPagination,
115
119
  },
116
120
  props: {
117
121
  autoWidth: Boolean,
@@ -124,8 +128,7 @@ export default {
124
128
  rowSize: {
125
129
  type: String,
126
130
  default: 'standard',
127
- validator: val =>
128
- ['compact', 'short', 'standard', 'tall', ''].includes(val),
131
+ validator: val => ['compact', 'short', 'standard', 'tall', ''].includes(val),
129
132
  },
130
133
  searchPlaceholder: { type: String, default: 'filter' },
131
134
  sortable: Boolean,
@@ -150,6 +153,7 @@ export default {
150
153
  batchActive: false,
151
154
  headingChecked: false,
152
155
  dataRowsSelected: this.rowsSelected,
156
+ registeredRows: [],
153
157
  };
154
158
  },
155
159
  watch: {
@@ -163,6 +167,11 @@ export default {
163
167
  this.updateRowsSelected();
164
168
  },
165
169
  },
170
+ created() {
171
+ // add these on created otherwise cv:mounted is too late.
172
+ this.$on('cv:mounted', srcComponent => this.onCvMount(srcComponent));
173
+ this.$on('cv:beforeDestroy', srcComponent => this.onCvBeforeDestroy(srcComponent));
174
+ },
166
175
  mounted() {
167
176
  console.warn(
168
177
  `${
@@ -176,15 +185,12 @@ export default {
176
185
  return this.$slots['batch-actions'];
177
186
  },
178
187
  hasOverflowMenu() {
179
- return (
180
- this.overflowMenu === true ||
181
- (this.overflowMenu && this.overflowMenu.length > 0)
182
- );
188
+ return this.overflowMenu === true || (this.overflowMenu && this.overflowMenu.length > 0);
183
189
  },
184
190
  tableStyle() {
185
191
  return this.autoWidth ? { width: 'initial' } : { width: '100%' };
186
192
  },
187
- internalPangination() {
193
+ internalPagination() {
188
194
  if (typeof this.pagination === 'object') {
189
195
  return this.pagination;
190
196
  } else {
@@ -195,24 +201,15 @@ export default {
195
201
  return false;
196
202
  },
197
203
  internalNumberOfItems() {
198
- if (
199
- this.internalPagination &&
200
- typeof this.internalPagination.numberOfItems === 'number'
201
- ) {
202
- return Math.min(
203
- this.internalPagination.numberOfItems,
204
- rows(this.$children).length
205
- );
204
+ if (this.internalPagination && typeof this.internalPagination.numberOfItems === 'number') {
205
+ return this.internalPagination.numberOfItems;
206
206
  } else {
207
- return rows(this.$children).length;
207
+ return this.registeredRows.length;
208
208
  }
209
209
  },
210
210
  modifierClasses() {
211
211
  const prefix = 'bx--data-table-v2--';
212
- const sizeClass =
213
- this.rowSize.length === 0 || this.rowSize === 'standard'
214
- ? ''
215
- : `${prefix}${this.rowSize} `;
212
+ const sizeClass = this.rowSize.length === 0 || this.rowSize === 'standard' ? '' : `${prefix}${this.rowSize} `;
216
213
  const zebraClass = this.zebra ? `${prefix}zebra ` : '';
217
214
  const borderlessClass = this.borderless ? `${prefix}no-border ` : '';
218
215
  return `${sizeClass}${zebraClass}${borderlessClass}`.trimRight();
@@ -228,6 +225,13 @@ export default {
228
225
  },
229
226
  },
230
227
  methods: {
228
+ onCvMount(row) {
229
+ this.registeredRows.push(row);
230
+ },
231
+ onCvBeforeDestroy(row) {
232
+ const index = this.registeredRows.findIndex(item => row.uid === item.uid);
233
+ this.registeredRows.slice(index, 1);
234
+ },
231
235
  updateRowsSelected() {
232
236
  this.dataRowsSelected = [];
233
237
  for (const i in this.$children) {
@@ -241,8 +245,7 @@ export default {
241
245
  }
242
246
  }
243
247
  this.headingChecked =
244
- this.dataRowsSelected.length ===
245
- this.$children.filter(item => item.isCvDataTableRow).length;
248
+ this.dataRowsSelected.length === this.$children.filter(item => item.isCvDataTableRow).length;
246
249
  this.batchActive = this.dataRowsSelected.length > 0;
247
250
  },
248
251
  onHeadingCheckChange() {
@@ -278,8 +281,7 @@ export default {
278
281
  modelSet.add(value);
279
282
  }
280
283
  this.dataRowsSelected = Array.from(modelSet);
281
- this.headingChecked =
282
- this.dataRowsSelected.length === rows(this.$children).length;
284
+ this.headingChecked = this.dataRowsSelected.length === rows(this.$children).length;
283
285
  this.batchActive = this.dataRowsSelected.length > 0;
284
286
 
285
287
  this.$emit('row-select-change', { value, selected: checked });
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <div class="cv-date-picker bx--form-item">
2
+ <cv-wrapper :tag-type="formItem ? 'div' : ''" class="cv-date-picker bx--form-item">
3
3
  <div
4
4
  :data-date-picker="['single', 'range'].includes(kind)"
5
5
  :data-date-picker-type="kind"
6
6
  class="bx--date-picker"
7
- :class="[kindClasses, { 'bx--date-picker--light': theme === 'light' }]"
7
+ :class="[kindClasses, { 'bx--date-picker--light': theme === 'light', 'cv-date-pciker': !formItem }]"
8
8
  ref="date-picker"
9
9
  >
10
10
  <div
@@ -14,7 +14,7 @@
14
14
  @change="onSimpleChange"
15
15
  >
16
16
  <svg
17
- v-if="kind === 'single'"
17
+ v-if="!componentsX && kind === 'single'"
18
18
  data-date-picker-icon
19
19
  class="bx--date-picker__icon"
20
20
  width="14"
@@ -27,46 +27,48 @@
27
27
  fill-rule="nonzero"
28
28
  ></path>
29
29
  </svg>
30
- <label :for="`${uid}-input-1`" class="bx--label">
31
- {{ getDateLabel }}
32
- </label>
33
- <input
34
- :data-invalid="isInvalid"
35
- type="text"
36
- :id="`${uid}-input-1`"
37
- class="bx--date-picker__input"
38
- :pattern="pattern"
39
- :placeholder="placeholder"
40
- data-date-picker-input
41
- :data-date-picker-input-from="kind === 'range'"
42
- ref="date"
43
- />
30
+ <label :for="`${uid}-input-1`" class="bx--label">{{ getDateLabel }}</label>
31
+ <cv-wrapper :tag-type="componentsX ? 'div' : ''" class="bx--date-picker-input__wrapper">
32
+ <input
33
+ :data-invalid="isInvalid"
34
+ type="text"
35
+ :id="`${uid}-input-1`"
36
+ class="bx--date-picker__input"
37
+ :pattern="pattern"
38
+ :placeholder="placeholder"
39
+ data-date-picker-input
40
+ :data-date-picker-input-from="kind === 'range'"
41
+ ref="date"
42
+ />
43
+ <Calendar16
44
+ v-if="componentsX && ['single', 'range'].includes(kind)"
45
+ class="bx--date-picker__icon"
46
+ data-date-picker-icon
47
+ @click="cal.open()"
48
+ />
49
+ </cv-wrapper>
44
50
  <div class="bx--form-requirement" v-if="isInvalid">
45
- <slot name="invalid-message">
46
- {{ invalidMessage || invalidDateMessage }}
47
- </slot>
51
+ <slot name="invalid-message">{{ invalidMessage || invalidDateMessage }}</slot>
48
52
  </div>
49
53
  </div>
50
- <div
51
- :class="{ 'bx--date-picker-container': kind === 'range' }"
52
- v-if="kind === 'range'"
53
- >
54
- <label :for="`${uid}-input-2`" class="bx--label">
55
- {{ getDateEndLabel }}
56
- </label>
57
- <input
58
- type="text"
59
- :id="`${uid}-input-2`"
60
- class="bx--date-picker__input"
61
- :pattern="pattern"
62
- :placeholder="placeholder"
63
- data-date-picker-input
64
- :data-date-picker-input-to="kind === 'range'"
65
- ref="todate"
66
- />
54
+ <div :class="{ 'bx--date-picker-container': kind === 'range' }" v-if="kind === 'range'">
55
+ <label :for="`${uid}-input-2`" class="bx--label">{{ getDateEndLabel }}</label>
56
+ <cv-wrapper :tag-type="componentsX ? 'div' : ''" class="bx--date-picker-input__wrapper">
57
+ <input
58
+ type="text"
59
+ :id="`${uid}-input-2`"
60
+ class="bx--date-picker__input"
61
+ :pattern="pattern"
62
+ :placeholder="placeholder"
63
+ data-date-picker-input
64
+ :data-date-picker-input-to="kind === 'range'"
65
+ ref="todate"
66
+ />
67
+ <Calendar16 v-if="componentsX" class="bx--date-picker__icon" data-date-picker-icon @click="cal.open()" />
68
+ </cv-wrapper>
67
69
  </div>
68
70
  <svg
69
- v-if="kind === 'range'"
71
+ v-if="!componentsX && kind === 'range'"
70
72
  data-date-picker-icon
71
73
  class="bx--date-picker__icon"
72
74
  width="14"
@@ -80,7 +82,7 @@
80
82
  ></path>
81
83
  </svg>
82
84
  </div>
83
- </div>
85
+ </cv-wrapper>
84
86
  </template>
85
87
 
86
88
  <script>
@@ -89,6 +91,9 @@ import l10n from 'flatpickr/dist/l10n/index';
89
91
  import RangePlugin from 'flatpickr/dist/plugins/rangePlugin';
90
92
  import uidMixin from '../../mixins/uid-mixin';
91
93
  import themeMixin from '../../mixins/theme-mixin';
94
+ import Calendar16 from '@carbon/icons-vue/lib/calendar/16';
95
+ import { componentsX } from '../../internal/feature-flags';
96
+ import CvWrapper from '../cv-wrapper/_cv-wrapper';
92
97
 
93
98
  // Weekdays shorthand for english locale
94
99
  l10n.en.weekdays.shorthand.forEach((day, index) => {
@@ -103,9 +108,11 @@ l10n.en.weekdays.shorthand.forEach((day, index) => {
103
108
  export default {
104
109
  name: 'CvDatePicker',
105
110
  mixins: [uidMixin, themeMixin],
111
+ components: { Calendar16, CvWrapper },
106
112
  props: {
107
113
  dateLabel: String,
108
114
  dateEndLabel: String,
115
+ formItem: { type: Boolean, default: true },
109
116
  kind: {
110
117
  type: String,
111
118
  default: 'simple',
@@ -126,9 +133,7 @@ export default {
126
133
  default: undefined,
127
134
  validator(val) {
128
135
  if (val !== undefined) {
129
- console.warn(
130
- 'CvDatePicker: invalid prop deprecated in favour of invalidMessage'
131
- );
136
+ console.warn('CvDatePicker: invalid prop deprecated in favour of invalidMessage');
132
137
  }
133
138
  return true;
134
139
  },
@@ -138,15 +143,16 @@ export default {
138
143
  default: null,
139
144
  validator(val) {
140
145
  if (val !== null) {
141
- console.warn(
142
- 'CvDatePicker: invalidDateMessage deprecated in favour of invalidMessage'
143
- );
146
+ console.warn('CvDatePicker: invalidDateMessage deprecated in favour of invalidMessage');
144
147
  }
145
148
  return true;
146
149
  },
147
150
  },
148
151
  invalidMessage: { type: String, default: null },
149
152
  },
153
+ data() {
154
+ return { componentsX };
155
+ },
150
156
  computed: {
151
157
  kindClasses() {
152
158
  if (this.kind === 'short') {
@@ -181,7 +187,7 @@ export default {
181
187
 
182
188
  // add events update based on parameters
183
189
  _options.onChange = this.onChange;
184
- _options.onValueUpdate = this.onChange;
190
+ // _options.onValueUpdate = this.onChange;
185
191
 
186
192
  if (this.kind === 'range') {
187
193
  _options.plugins = [new RangePlugin({ input: this.$refs.todate })];
@@ -191,7 +197,7 @@ export default {
191
197
  },
192
198
  isInvalid() {
193
199
  return (
194
- this.$slots['invalid-message'] ||
200
+ this.$slots['invalid-message'] !== undefined ||
195
201
  (this.invalidMessage && this.invalidMessage.length) ||
196
202
  (this.invalidDateMessage && this.invalidDateMessage.length)
197
203
  );
@@ -0,0 +1,226 @@
1
+ <!--
2
+ Carbon Style Dropdown
3
+
4
+ Attributes:
5
+ placeholder: Placeholder text displayed before any selection is made.
6
+
7
+ Usage:
8
+ <cv-dropdown placeholder="Choose an option">
9
+ <cv-dropdown-item value="10">Option 1</cv-dropdown-item>
10
+ <cv-dropdown-item value="20">Option 2</cv-dropdown-item>
11
+ <cv-dropdown-item value="30">Option 3</cv-dropdown-item>
12
+ <cv-dropdown-item value="40">Option 4</cv-dropdown-item>
13
+ <cv-dropdown-item value="50">Option 5</cv-dropdown-item>
14
+ </cv-dropdown>
15
+
16
+ -->
17
+
18
+ <template>
19
+ <cv-wrapper :tag-type="formItem ? 'div' : ''" class="cv-dropdown bx--form-item">
20
+ <div class="bx--dropdown__wrapper" :class="{ 'bx--dropdown__wrapper--inline': inline, 'cv-dropdown': !formItem }">
21
+ <label :for="uid" class="bx--label" :class="{ 'bx--label--disabled': $attrs.disabled }">{{ label }}</label>
22
+
23
+ <div
24
+ v-if="!inline && isHelper"
25
+ class="bx--form__helper-text"
26
+ :class="{ 'bx--form__helper-text--disabled': $attrs.disabled }"
27
+ >
28
+ <slot name="helper-text">{{ helperText }}</slot>
29
+ </div>
30
+
31
+ <ul
32
+ data-dropdown
33
+ :data-value="internalValue"
34
+ :data-invalid="isInvalid"
35
+ class="bx--dropdown"
36
+ tabindex="0"
37
+ :class="{
38
+ 'bx--dropdown--light': theme === 'light',
39
+ 'bx--dropdown--up': up,
40
+ 'bx--dropdown--open': open,
41
+ 'bx--dropdown--invalid': isInvalid,
42
+ 'bx--dropdown--disabled': $attrs.disabled,
43
+ }"
44
+ v-bind="$attrs"
45
+ @keydown.down.prevent="onDown"
46
+ @keydown.up.prevent="onUp"
47
+ @keydown.enter.prevent="onClick"
48
+ @keydown.esc.prevent="onEsc"
49
+ @click="onClick"
50
+ >
51
+ <WarningFilled16 v-if="isInvalid" class="bx--dropdown__invalid-icon" />
52
+ <li v-if="inline" class="bx--dropdown-text" ref="valueContent">
53
+ <span class="bx--dropdown-text__inner">{{ placeholder }}</span>
54
+ <chevron-down-16 class="bx--dropdown__arrow" />
55
+ </li>
56
+ <template v-else>
57
+ <li class="bx--dropdown-text" ref="valueContent">{{ placeholder }}</li>
58
+ <li class="bx--dropdown__arrow-container">
59
+ <chevron-down-16 class="bx--dropdown__arrow" />
60
+ </li>
61
+ </template>
62
+
63
+ <li>
64
+ <ul class="bx--dropdown-list">
65
+ <slot></slot>
66
+ </ul>
67
+ </li>
68
+ </ul>
69
+ <div v-if="isInvalid" class="bx--form-requirement">
70
+ <slot name="invalid-message">{{ invalidMessage }}</slot>
71
+ </div>
72
+ </div>
73
+ <div v-if="isInvalid" class="bx--form-requirement">
74
+ <slot name="invalid-message">{{ invalidMessage }}</slot>
75
+ </div>
76
+ </cv-wrapper>
77
+ </template>
78
+
79
+ <script>
80
+ import uidMixin from '../../mixins/uid-mixin';
81
+ import themeMixin from '../../mixins/theme-mixin';
82
+ import WarningFilled16 from '@carbon/icons-vue/lib/warning--filled/16';
83
+ import ChevronDown16 from '@carbon/icons-vue/lib/chevron--down/16';
84
+ import CvWrapper from '../cv-wrapper/_cv-wrapper';
85
+
86
+ export default {
87
+ name: 'CvDropdownInner',
88
+ inheritAttrs: false,
89
+ mixins: [uidMixin, themeMixin],
90
+ components: { WarningFilled16, ChevronDown16, CvWrapper },
91
+ props: {
92
+ formItem: { type: Boolean, default: true },
93
+ inline: Boolean,
94
+ invalidMessage: { type: String, default: null },
95
+ helperText: { type: String, default: null },
96
+ label: String,
97
+ placeholder: {
98
+ type: String,
99
+ default: 'Choose an option',
100
+ },
101
+ up: Boolean,
102
+ value: String, // initial value of the dropdown,
103
+ },
104
+ data() {
105
+ return {
106
+ open: false,
107
+ dataValue: this.value,
108
+ };
109
+ },
110
+ mounted() {
111
+ console.dir(this.$attrs);
112
+ this.$el.addEventListener('focusout', ev => {
113
+ if (ev.relatedTarget === null || !this.$el.contains(ev.relatedTarget)) {
114
+ this.open = false;
115
+ }
116
+ });
117
+ this.internalValue = this.internalValue; // forces update of value
118
+ },
119
+ model: {
120
+ prop: 'value',
121
+ event: 'change',
122
+ },
123
+ watch: {
124
+ value(val) {
125
+ this.internalValue = val;
126
+ },
127
+ },
128
+ computed: {
129
+ isInvalid() {
130
+ return this.$slots['invalid-message'] !== undefined || (this.invalidMessage && this.invalidMessage.length);
131
+ },
132
+ isHelper() {
133
+ return this.$slots['helper-text'] !== undefined || (this.helperText && this.helperText.length);
134
+ },
135
+ internalValue: {
136
+ get() {
137
+ return this.dataValue;
138
+ },
139
+ set(val) {
140
+ const childItems = this.dropdownItems();
141
+ for (let index in childItems) {
142
+ let child = childItems[index];
143
+ let selected = child.value === val;
144
+ child.internalSelected = selected;
145
+
146
+ if (selected) {
147
+ this.$refs.valueContent.innerHTML = child.internalContent;
148
+ }
149
+ }
150
+ if (this.dataValue !== val) {
151
+ // only raise event on change
152
+ this.dataValue = val;
153
+ this.$emit('change', this.dataValue);
154
+ }
155
+ },
156
+ },
157
+ },
158
+ methods: {
159
+ dropdownItems() {
160
+ return this.$children.filter(item => item.$_CvDropdownItem);
161
+ },
162
+ doMove(up) {
163
+ // requery could have changed
164
+ let currentFocusEl = this.$el.querySelector('.cv-dropdown-item :focus');
165
+ let currentFocusValue;
166
+ let childItems = this.dropdownItems();
167
+ let last = childItems.length - 1;
168
+ let currentFocusIndex = up ? 0 : last;
169
+ let nextFocusIndex;
170
+
171
+ if (currentFocusEl) {
172
+ currentFocusValue = currentFocusEl.parentNode.getAttribute('data-value');
173
+ }
174
+
175
+ if (currentFocusValue !== undefined) {
176
+ currentFocusIndex = childItems.findIndex(child => child.value === currentFocusValue);
177
+ }
178
+
179
+ if (up) {
180
+ nextFocusIndex = currentFocusIndex > 0 ? currentFocusIndex - 1 : last;
181
+ if (childItems[nextFocusIndex].internalSelected) {
182
+ nextFocusIndex = nextFocusIndex > 0 ? nextFocusIndex - 1 : last;
183
+ }
184
+ } else {
185
+ nextFocusIndex = currentFocusIndex < last ? currentFocusIndex + 1 : 0;
186
+ if (childItems[nextFocusIndex].internalSelected) {
187
+ nextFocusIndex = nextFocusIndex < last ? nextFocusIndex + 1 : 0;
188
+ }
189
+ }
190
+
191
+ childItems[nextFocusIndex].setFocus();
192
+ },
193
+ onDown() {
194
+ if (!this.open) {
195
+ this.open = true;
196
+ } else {
197
+ this.doMove(false);
198
+ }
199
+ },
200
+ onUp() {
201
+ if (this.open) {
202
+ this.doMove(true);
203
+ }
204
+ },
205
+ onEsc() {
206
+ this.open = false;
207
+ this.$el.focus();
208
+ },
209
+ onClick(ev) {
210
+ this.open = !this.open;
211
+ if (!this.open) {
212
+ this.$el.focus();
213
+ }
214
+
215
+ if (ev.target.classList.contains('bx--dropdown-link')) {
216
+ const targetItemEl = ev.target.parentNode;
217
+ const newValue = targetItemEl.getAttribute('data-value');
218
+
219
+ this.internalValue = newValue;
220
+ }
221
+ },
222
+ },
223
+ };
224
+ </script>
225
+
226
+ <style lang="scss"></style>