@itfin/components 1.0.17 → 1.0.21

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.
@@ -30,9 +30,9 @@
30
30
  @import '~bootstrap/scss/transitions';
31
31
 
32
32
  .modal-open .itf-app {
33
- -webkit-filter: blur(3px);
34
- -moz-filter: blur(3px);
35
- filter: blur(3px);
33
+ -webkit-filter: blur(5px);
34
+ -moz-filter: blur(5px);
35
+ filter: blur(5px);
36
36
  }
37
37
  </style>
38
38
  <script>
@@ -40,7 +40,7 @@
40
40
  :aria-label="`Deselect ${getOptionLabel(option)}`"
41
41
  @click="deselect(option)"
42
42
  >
43
- <deselect />
43
+ <itf-icon name="close" />
44
44
  </itf-button>
45
45
  </span>
46
46
  </slot>
@@ -66,17 +66,18 @@
66
66
  aria-label="Clear Selected"
67
67
  @click="clearSelection"
68
68
  >
69
- <deselect />
69
+ <itf-icon name="close" />
70
70
  </itf-button>
71
71
 
72
72
  <slot name="open-indicator" v-bind="scope.openIndicator">
73
73
  <itf-button
74
+ tabindex="-1"
74
75
  small
75
76
  icon
76
77
  v-if="!noDrop && !mutableLoading && !disabled"
77
78
  v-bind="scope.openIndicator.attributes"
78
79
  >
79
- <open-indicator />
80
+ <itf-icon name="chevron_down" />
80
81
  </itf-button>
81
82
  </slot>
82
83
 
@@ -178,7 +179,8 @@
178
179
  }
179
180
  }
180
181
  &.vs--multiple .form-control {
181
- padding-top: 1px;
182
+ padding-top: 0.125rem;
183
+ padding-bottom: 0.125rem;
182
184
  }
183
185
  &.vs--open .form-control {
184
186
  box-shadow: 0 0 0 0.125rem $input-focus-border;
@@ -193,9 +195,7 @@
193
195
  }
194
196
  .dropdown-menu {
195
197
  background-color: $body-bg;
196
- border-left: 2px solid $input-focus-border;
197
- border-right: 2px solid $input-focus-border;
198
- border-bottom: 2px solid $input-focus-border;
198
+ border: 2px solid $input-focus-border;
199
199
  padding-left: 0.125rem;
200
200
  padding-right: 0.125rem;
201
201
  left: -2px;
@@ -204,9 +204,7 @@
204
204
 
205
205
  @media (prefers-color-scheme: notdark) {
206
206
  background-color: $dark-body-bg;
207
- border-left-color: $dark-input-focus-border;
208
- border-right-color: $dark-input-focus-border;
209
- border-bottom-color: $dark-input-focus-border;
207
+ border-color: $dark-input-focus-border;
210
208
  }
211
209
 
212
210
  .dropdown-item {
@@ -226,8 +224,7 @@
226
224
  import pointerScroll from '../../mixins/pointerScroll';
227
225
  import typeAheadPointer from '../../mixins/typeAheadPointer';
228
226
  import ajax from '../../mixins/ajax';
229
- import Deselect from './Deselect';
230
- import OpenIndicator from './OpenIndicator';
227
+ import itfIcon from '../icon/Icon';
231
228
  import itfButton from '../button/Button';
232
229
  import appendToBody from '../../directives/appendToBody';
233
230
  import sortAndStringify from '../../helpers/sortAndStringify';
@@ -237,9 +234,8 @@ import uniqueId from '../../helpers/uniqueId';
237
234
  */
238
235
  export default {
239
236
  components: {
240
- Deselect,
241
- OpenIndicator,
242
237
  itfButton,
238
+ itfIcon
243
239
  },
244
240
  directives: { appendToBody },
245
241
  mixins: [pointerScroll, typeAheadPointer, ajax],
@@ -585,6 +581,14 @@ export default {
585
581
  return clearSearchOnSelect && !multiple
586
582
  },
587
583
  },
584
+ /**
585
+ * Disable the dropdown entirely.
586
+ * @type {Boolean}
587
+ */
588
+ opened: {
589
+ type: Boolean,
590
+ default: false,
591
+ },
588
592
  /**
589
593
  * Disable the dropdown entirely.
590
594
  * @type {Boolean}
@@ -866,7 +870,7 @@ export default {
866
870
  * @return {Boolean} True if open
867
871
  */
868
872
  dropdownOpen() {
869
- return this.dropdownShouldOpen(this)
873
+ return this.opened || this.dropdownShouldOpen(this);
870
874
  },
871
875
  /**
872
876
  * Return the placeholder string if it's set
@@ -32,7 +32,8 @@ storiesOf('Common', module)
32
32
  countries: [
33
33
  {code: 'CA', country: 'Canada'},
34
34
  {code: 'US', country: 'United states'},
35
- {code: 'UA', country: 'Ukraine'}
35
+ {code: 'UA', country: 'Ukraine'},
36
+ {code: 'TEST', country: 'ASdasd sdas dasd sad sadas da ad asdsad ad d ada dsadsadsadasd sadsa das dasd asdasd asdas da '}
36
37
  ]
37
38
  }
38
39
  },
@@ -75,6 +76,12 @@ storiesOf('Common', module)
75
76
  :reduce="country => country.code"
76
77
  v-model="selected" label="country" :options="countries"></itf-select>
77
78
 
79
+ <itf-select
80
+ loading
81
+ placeholder="Country"
82
+ :reduce="country => country.code"
83
+ v-model="selected" label="country" :options="countries"></itf-select>
84
+
78
85
  </itf-label>
79
86
  </div>
80
87
  <div class="col-6">
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <span>
3
+ <slot name="activator"></slot>
4
+ <div style="display: none"><div ref="tooltip"><slot></slot></div></div>
5
+ </span>
6
+ </template>
7
+ <script>
8
+ import { Vue, Component, Model, Inject, Prop } from 'vue-property-decorator';
9
+ import tippy from 'tippy.js';
10
+ import '../../assets/scss/directives/tooltip.scss';
11
+
12
+ export default @Component({
13
+ name: 'itfTooltip',
14
+ components: {
15
+ }
16
+ })
17
+ class itfTooltip extends Vue {
18
+ @Prop({ type: String, default: 'bottom', validator: (value) => [
19
+ 'bottom-start', 'bottom-end', 'bottom',
20
+ 'left', 'right',
21
+ 'top-start', 'top-end', 'top'
22
+ ].includes(value) }) placement;
23
+
24
+ mounted() {
25
+ if (typeof document === 'undefined') {
26
+ return;
27
+ }
28
+ this.tooltip = tippy(this.$el, {
29
+ content: this.$refs.tooltip,
30
+ allowHTML: true,
31
+ theme: 'itfin',
32
+ interactive: true,
33
+ delay: 500,
34
+ placement: this.placement,
35
+ appendTo: document.body,
36
+ });
37
+ }
38
+ }
39
+ </script>
@@ -0,0 +1,52 @@
1
+ import { storiesOf } from '@storybook/vue';
2
+ import itfApp from '../app/App.vue';
3
+ import itfButton from '../button/Button.vue';
4
+ import itfIcon from '../icon/Icon.vue';
5
+ import itfTooltip from './Tooltip.vue';
6
+ import tooltip from '../../directives/tooltip';
7
+
8
+ storiesOf('Common', module)
9
+ .add('Tooltip', () => ({
10
+ components: {
11
+ itfApp,
12
+ itfIcon,
13
+ itfTooltip,
14
+ itfButton
15
+ },
16
+ directives: {
17
+ tooltip
18
+ },
19
+ data() {
20
+ return {
21
+ tooltip: 'Test tooltip'
22
+ }
23
+ },
24
+ template: `<div>
25
+ <p>You need wrap whole application with this tag</p>
26
+
27
+ <h2>Usage</h2>
28
+
29
+ <pre>
30
+ &lt;button v-tooltip="'Test tooltip'">Test&lt;/button>
31
+ &lt;button v-tooltip.delay="'Test tooltip with delay'">Test&lt;/button>
32
+ </pre>
33
+
34
+ <h3>Example</h3>
35
+
36
+ <itf-app>
37
+
38
+ <button v-tooltip="tooltip">Tooltip test</button>
39
+
40
+ <button v-tooltip.delay="tooltip">Tooltip test with delay</button>
41
+
42
+ <itf-tooltip>
43
+ <template #activator="{ on }">
44
+ <a href="" v-on="on">Test</a>
45
+ </template>
46
+
47
+ <span>Test</span>
48
+ </itf-tooltip>
49
+
50
+ </itf-app>
51
+ </div>`,
52
+ }));
@@ -0,0 +1,82 @@
1
+ <template>
2
+
3
+ <div class="itf-tree-view d-grid">
4
+ <itf-button
5
+ @click="onSelect(node)"
6
+ v-for="({ node, depth }, n) of list"
7
+ :key="n"
8
+ :class="{ [`depth-${depth}`]: true, 'active': value === node[itemKey] }"
9
+ class="align-items-center d-flex gap-2 py-2" aria-current="true">
10
+ <slot name="prepend" :item="node"></slot>
11
+ <div class="d-flex align-items-start gap-2 w-100 justify-content-between">
12
+ <div>
13
+ <slot name="item" :item="node"></slot>
14
+ </div>
15
+ <slot name="append" :item="node"></slot>
16
+ </div>
17
+ </itf-button>
18
+ </div>
19
+
20
+ </template>
21
+ <style lang="scss">
22
+ @import '../../assets/scss/variables';
23
+ @import '~bootstrap/scss/list-group';
24
+
25
+ .itf-tree-view {
26
+ .itf-button {
27
+ &.depth-0 {
28
+ padding-left: $spacer;
29
+ }
30
+
31
+ &.depth-1 {
32
+ padding-left: $spacer * 2;
33
+ }
34
+
35
+ &.depth-2 {
36
+ padding-left: $spacer * 3;
37
+ }
38
+ }
39
+ }
40
+ </style>
41
+ <script>
42
+ import { Vue, Component, Model, Inject, Prop } from 'vue-property-decorator';
43
+ import itfIcon from '../icon/Icon';
44
+ import itfButton from '../button/Button';
45
+
46
+ export default @Component({
47
+ name: 'itfTreeView',
48
+ components: {
49
+ itfIcon,
50
+ itfButton
51
+ }
52
+ })
53
+ class itfTreeView extends Vue {
54
+ @Inject({ default: null }) itemLabel;
55
+ @Model('input') value;
56
+ @Prop(Array) items;
57
+ @Prop({ type: String, default: 'Id' }) itemKey;
58
+ @Prop({ type: String, default: 'ParentId' }) itemParentKey;
59
+
60
+ onSelect(item) {
61
+ this.$emit('input', item[this.itemKey]);
62
+ }
63
+
64
+ get list() {
65
+ if (!this.items || !this.items.length) {
66
+ return [];
67
+ }
68
+ const root = this.items.find((item) => item[this.itemKey] === null);
69
+ if (!root) {
70
+ throw new Error('Root not found');
71
+ }
72
+ function fillListWithNodes(items, node, itemKey, parentKey, depth = 0) {
73
+ const list = items
74
+ .filter((item) => item[parentKey] === node[itemKey])
75
+ // eslint-disable-next-line max-len
76
+ .reduce((acc, childNode) => acc.concat(fillListWithNodes(items, childNode, itemKey, parentKey, depth + 1)), []);
77
+ return [{ node, depth }, ...list];
78
+ }
79
+ return fillListWithNodes(this.items, root, this.itemKey, this.itemParentKey);
80
+ }
81
+ }
82
+ </script>
@@ -0,0 +1,86 @@
1
+ import { storiesOf } from '@storybook/vue';
2
+ import itfApp from '../app/App.vue';
3
+ import itfButton from '../button/Button.vue';
4
+ import itfIcon from '../icon/Icon.vue';
5
+ import itfTreeView from './TreeView.vue';
6
+
7
+ storiesOf('Tree', module)
8
+ .add('Tree View', () => ({
9
+ components: {
10
+ itfApp,
11
+ itfIcon,
12
+ itfTreeView,
13
+ itfButton
14
+ },
15
+ data() {
16
+ return {
17
+ selected: null,
18
+ items: [
19
+ { Name: 'CEO', Id: null, Count: 69, Responsible: { FirstName: 'Anatolii', LastName: 'Maslov' } },
20
+ { Name: 'Delivery', Id: 1, ParentId: null, Count: 50, Type: 'Dep', Responsible: { FirstName: 'Andrii', LastName: 'Olek' } },
21
+ { Name: 'Marketing', Id: 2, ParentId: null, Count: 1, Type: 'Dep', Responsible: { FirstName: 'Emil', LastName: 'Tro' } },
22
+ { Name: 'Sales', Id: 3, ParentId: null, Count: 4, Type: 'Dep', Responsible: { FirstName: 'Yurii', LastName: 'Ko' } },
23
+ { Name: 'Salesman', Id: 4, ParentId: 3, Count: 2, Type: 'Team' },
24
+ { Name: 'Searchers', Id: 5, ParentId: 3, Count: 2, Type: 'Team' },
25
+ { Name: 'PMO', Id: 6, ParentId: 1, Count: 69, Type: 'Dep' },
26
+ ]
27
+ }
28
+ },
29
+ template: `<div>
30
+ <p>You need wrap whole application with this tag</p>
31
+
32
+ <h2>Usage</h2>
33
+
34
+ <pre>
35
+ &lt;itf-table
36
+ :columns="columns"
37
+ :rows="list"
38
+ >
39
+ &lt;template #column.Employee="&#123; item }">&lt;/template>
40
+ &lt;/itf-table>
41
+ </pre>
42
+
43
+ <h3>Example</h3>
44
+
45
+ <itf-app>
46
+ <div class="row">
47
+ <div class="col-3">
48
+ <itf-tree-view :items="items" v-model="selected">
49
+ <template #item="{ item }">
50
+ <h6 class="mb-0">
51
+ <span>
52
+ {{item.Name}}
53
+ </span>
54
+ </h6>
55
+ <p v-if="item.Responsible" class="mb-0 opacity-75">
56
+ <small>
57
+ {{item.Responsible.FirstName}}
58
+ {{item.Responsible.LastName}}
59
+ </small>
60
+ </p>
61
+ </template>
62
+ <template #prepend="{ item }">
63
+ <itf-icon v-if="!item.Id" name="globe" size="24" class="flex-shrink-0" />
64
+ <itf-icon v-else-if="item.Type === 'Dep'" name="folder_closed" size="24" class="flex-shrink-0" />
65
+ <itf-icon v-else-if="item.Type === 'Team'" name="users" size="24" class="flex-shrink-0" />
66
+ </template>
67
+ <template #append="{ item }">
68
+ <small class="opacity-50 text-nowrap">
69
+ <itf-icon name="user_male" /> {{item.Count}}
70
+ </small>
71
+ </template>
72
+ </itf-tree-view>
73
+ </div>
74
+ <div class="col-9">
75
+
76
+ <h1 class="align-items-center d-flex">
77
+ <itf-icon class="me-2" name="globe" size="36" />
78
+ The Company
79
+ </h1>
80
+
81
+ {{selected}}
82
+ </div>
83
+ </div>
84
+ </itf-app>
85
+ </div>`,
86
+ }));
@@ -63,31 +63,6 @@ storiesOf('Common', module)
63
63
  </itf-app>
64
64
  </div>`,
65
65
  }))
66
- .add('Tooltip', () => ({
67
- directives: {
68
- tooltip,
69
- },
70
- components: { itfApp },
71
- data() {
72
- return { date: '2021-01-16', tooltip: 'This button run payroll calculation <br/><a href="/" target="_blank">Read more</a>' }
73
- },
74
- template: `<div>
75
- <h2>Usage</h2>
76
-
77
- <pre>
78
-
79
- &lt;button v-tooltip="'Test tooltip'">Test&lt;/button>
80
- &lt;button v-tooltip.delay="'Test tooltip with delay'">Test&lt;/button>
81
- </pre>
82
-
83
- <h2>Example</h2>
84
-
85
- <button v-tooltip="tooltip">Tooltip test</button>
86
-
87
- <button v-tooltip.delay="tooltip">Tooltip test with delay</button>
88
-
89
- </div>`,
90
- }))
91
66
  .add('Inline loader', () => ({
92
67
  directives: {
93
68
  loading,
@@ -1,49 +0,0 @@
1
- :root {
2
- --loader-color: #1967d2; }
3
-
4
- .com-loading-wrapper {
5
- pisition: relative; }
6
- .com-loading-wrapper .com-loading {
7
- position: absolute;
8
- background-color: rgba(255, 255, 255, 0.4);
9
- top: 0;
10
- left: 0;
11
- right: 0;
12
- bottom: 0;
13
- z-index: 10;
14
- color: var(--loader-color); }
15
- .com-loading-wrapper .com-loading-message {
16
- text-align: center;
17
- position: absolute;
18
- top: 50%;
19
- left: 50%;
20
- margin-top: -16px;
21
- margin-left: -16px; }
22
- .com-loading-wrapper .com-loading-icon {
23
- -webkit-animation: spinner 2s linear 0s infinite;
24
- animation: spinner 2s linear 0s infinite;
25
- width: 32px;
26
- height: 32px;
27
- display: inline-block; }
28
- .com-loading-wrapper .com-loading-icon > svg {
29
- fill: currentColor;
30
- object-fit: contain;
31
- width: 100%;
32
- height: 100%;
33
- display: block; }
34
-
35
- @-webkit-keyframes spinner {
36
- 0% {
37
- -webkit-transform: rotate(0deg);
38
- transform: rotate(0deg); }
39
- 100% {
40
- -webkit-transform: rotate(360deg);
41
- transform: rotate(360deg); } }
42
-
43
- @keyframes spinner {
44
- 0% {
45
- -webkit-transform: rotate(0deg);
46
- transform: rotate(0deg); }
47
- 100% {
48
- -webkit-transform: rotate(360deg);
49
- transform: rotate(360deg); } }
@@ -1,3 +0,0 @@
1
- <template>
2
- <svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="m7.5 7.5 6 6"/><path d="m13.5 7.5-6 6"/></g></svg>
3
- </template>
@@ -1,3 +0,0 @@
1
- <template>
2
- <svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><path d="m8.5.5-4 4-4-4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(6 8)"/></svg>
3
- </template>