@itfin/components 1.0.64 → 1.0.68

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itfin/components",
3
- "version": "1.0.64",
3
+ "version": "1.0.68",
4
4
  "main": "dist/itfin-components.umd.js",
5
5
  "unpkg": "dist/itfin-components.common.js",
6
6
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
@@ -2,9 +2,11 @@
2
2
 
3
3
  <div class="itf-checkbox form-check" :class="{ 'form-switch': this.switch, 'itf-checkbox__large': large, 'itf-checkbox__medium': medium }">
4
4
  <input class="form-check-input" :id="id" type="checkbox" name="checkbox" v-model="isChecked" :disabled="isDisabled" />
5
- <label :for="id" slot="label" class="form-check-label">
6
- {{label}}
7
- <slot name="icon"></slot>
5
+ <label :for="id" class="form-check-label">
6
+ <slot name="label">
7
+ {{label}}
8
+ <slot name="icon"></slot>
9
+ </slot>
8
10
  </label>
9
11
  </div>
10
12
 
@@ -1,18 +1,19 @@
1
1
  <template>
2
2
  <div class="itf-segmeneted-control">
3
- <span v-if="!isUndefined" class="selection" :class="{'elevation-1': !disabled}"></span>
3
+ <span v-if="!isUndefined" ref="slider" class="selection" :class="{'elevation-1': !disabled}"></span>
4
4
 
5
5
  <div class="option" v-for="(item, n) in itemsWithNames" :key="n">
6
6
  <label>
7
7
  <input
8
+ ref="input"
8
9
  type="radio"
9
10
  :name="name"
10
11
  :value="n"
11
12
  :checked="isChecked(item)"
12
13
  @change="onItemChanged(item)" />
13
14
  <span>
14
- <slot name="item" :item="item" :itemKey="n">{{item[itemText]}}</slot>
15
- </span>
15
+ <slot name="item" :item="item" :itemKey="n">{{item[itemText]}}</slot>
16
+ </span>
16
17
  </label>
17
18
  </div>
18
19
  </div>
@@ -203,32 +204,30 @@ class itfSegmentedControl extends Vue {
203
204
  }
204
205
 
205
206
  init() {
206
- const INDIVIDUAL_SEGMENT_SELECTOR = '.option input';
207
- const BACKGROUND_PILL_SELECTOR = '.selection';
207
+ this.$el.addEventListener('change', () => updatePillPosition(this));
208
+ //window.addEventListener('resize', () => updatePillPosition(this.$el)); // Prevent pill from detaching from element when window resized. Becuase this is rare I haven't bothered with throttling the event
208
209
 
209
- this.$el.addEventListener('change', () => updatePillPosition(this.$el));
210
- window.addEventListener('resize', () => updatePillPosition(this.$el)); // Prevent pill from detaching from element when window resized. Becuase this is rare I haven't bothered with throttling the event
210
+ updatePillPosition(this);
211
211
 
212
- updatePillPosition(this.$el);
213
-
214
- function updatePillPosition (el) {
215
- forEachElement(el, INDIVIDUAL_SEGMENT_SELECTOR, (elem, index) => {
212
+ function updatePillPosition (component) {
213
+ component.$refs.input.forEach((elem, index) => {
216
214
  if (elem.checked) {
217
- moveBackgroundPillToElement(el, elem, index);
215
+ component.$nextTick(() => moveBackgroundPillToElement(component, elem, index));
216
+ setTimeout(() => moveBackgroundPillToElement(component, elem, index), 500);
217
+ setTimeout(() => moveBackgroundPillToElement(component, elem, index), 750);
218
+ setTimeout(() => moveBackgroundPillToElement(component, elem, index), 1500);
219
+ setTimeout(() => moveBackgroundPillToElement(component, elem, index), 3000);
218
220
  }
219
221
  })
220
222
  }
221
223
 
222
- function moveBackgroundPillToElement (el, elem, index) {
223
- const slider = el.querySelector(BACKGROUND_PILL_SELECTOR);
224
+ function moveBackgroundPillToElement (component, elem, index) {
225
+ const slider = component.$refs.slider;
226
+ console.info('init 2', slider);
224
227
  if (slider) {
225
228
  slider.style.transform = 'translateX(' + (elem.offsetWidth * index) + 'px)';
226
229
  }
227
230
  }
228
-
229
- function forEachElement(el, className, fn) {
230
- Array.from(el.querySelectorAll(className)).forEach(fn);
231
- }
232
231
  }
233
232
 
234
233
  onItemChanged (item) {
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <a :href="href" class="itf-tab" :data-test="`itf-tab-${id || _id}`" @click="setActive" :class="{ active }">
3
+ <span><slot /></span>
4
+ </a>
5
+ </template>
6
+ <style lang="scss" scoped>
7
+ .itf-tab {
8
+
9
+ }
10
+ </style>
11
+ <script>
12
+ import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
13
+
14
+ export default @Component({
15
+ name: 'itfTab',
16
+ components: {
17
+ }
18
+ })
19
+ class itfTab extends Vue {
20
+ @Inject() tabsManager;
21
+ @Prop() id;
22
+ @Prop() to;
23
+
24
+ get href() {
25
+ if (this.to) {
26
+ const route = this.$router.resolve(this.to);
27
+ return route && route.href;
28
+ }
29
+ return 'javascript:;';
30
+ }
31
+
32
+ get active() {
33
+ if (this.to) {
34
+ const route = this.$router.resolve(this.to);
35
+ return this.$route.path === (route && route.href);
36
+ }
37
+ return this.tabsManager && this.tabsManager.getValue() === this.id;
38
+ }
39
+
40
+ setActive() {
41
+ if (this.to) {
42
+ this.$router.push(this.to);
43
+ }
44
+ this.tabsManager.setValue(this.id || this._id);
45
+ }
46
+ }
47
+ </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div v-show="tabsManager && tabsManager.getValue() === id" :class="{ filled }" class="itf-tab-content" :data-test="`itf-tab-content-${id || _id}`">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+ <style lang="scss" scoped>
7
+ .itf-tab-content {
8
+
9
+ }
10
+ </style>
11
+ <script>
12
+ import { Vue, Component, Prop, Inject } from 'vue-property-decorator';
13
+
14
+ export default @Component({
15
+ name: 'itfTabContent',
16
+ components: {
17
+ }
18
+ })
19
+ class itfTabContent extends Vue {
20
+ @Inject({ default: null }) tabsManager;
21
+ @Prop() id;
22
+ @Prop(Boolean) filled;
23
+ }
24
+ </script>
@@ -0,0 +1,66 @@
1
+ <script>
2
+ import {
3
+ Vue, Component, Provide, Model, Emit,
4
+ } from 'vue-property-decorator';
5
+ import itfTab from './Tab';
6
+ import './tabs.scss';
7
+
8
+ export default @Component({
9
+ name: 'itfTabs',
10
+ components: {
11
+ itfTab
12
+ },
13
+ })
14
+ class itfTabs extends Vue {
15
+ @Provide() tabsManager = this;
16
+
17
+ @Model('input') value;
18
+
19
+ tabNodes;
20
+
21
+ render(createElement) {
22
+ const [tabNodes, contents] = parseNodes(this.$slots);
23
+ this.tabNodes = tabNodes;
24
+
25
+ return createElement('div', { staticClass: 'itf-tabs' }, [
26
+ createElement('div', { staticClass: 'itf-tabs-panel' }, tabNodes.reverse()),
27
+ createElement('div', { staticClass: 'itf-tabs-content' }, contents)
28
+ ]);
29
+
30
+ function parseNodes(slots) {
31
+ const items = [];
32
+ const contents = [];
33
+ const nodes = (slots.default || []);
34
+
35
+ for (const vnode of nodes) {
36
+ if (!vnode.componentOptions) {
37
+ continue;
38
+ }
39
+ if (vnode.componentOptions.tag.includes('-tab-content')) {
40
+ contents.push(vnode);
41
+ continue;
42
+ }
43
+ if (vnode.componentOptions.tag.includes('-tab')) {
44
+ items.push(vnode);
45
+ }
46
+ }
47
+ return [items, contents];
48
+ }
49
+ }
50
+
51
+ @Emit('input')
52
+ setValue(value) {
53
+ this.tabNodes = null;
54
+ }
55
+
56
+ getValue() {
57
+ return this.value;
58
+ }
59
+
60
+ scrollToTop() {
61
+ this.$el.scrollIntoView({
62
+ behavior: 'smooth',
63
+ });
64
+ }
65
+ }
66
+ </script>
@@ -0,0 +1,66 @@
1
+ import { storiesOf } from '@storybook/vue';
2
+ import itfApp from '../app/App.vue';
3
+ import itfButton from '../button/Button.vue';
4
+ import itfTab from './Tab.vue';
5
+ import itfTabContent from './TabContent.vue';
6
+ import itfTabs from './Tabs.vue';
7
+ import itfLabel from '../form/Label.vue';
8
+ import itfDatePicker from '../datepicker/DatePicker.vue';
9
+ import itfDateRangePicker from '../datepicker/DateRangePicker.vue';
10
+
11
+ storiesOf('Common', module)
12
+ .add('Tabs', () => ({
13
+ components: {
14
+ itfApp,
15
+ itfTabs,
16
+ itfLabel,
17
+ itfDatePicker,
18
+ itfDateRangePicker,
19
+ itfTab,
20
+ itfTabContent,
21
+ itfButton
22
+ },
23
+ data() {
24
+ return {
25
+ tab: 'Test1'
26
+ }
27
+ },
28
+ methods: {
29
+ },
30
+ template: `<div>
31
+ <p>You need wrap whole application with this tag</p>
32
+
33
+ <h2>Usage</h2>
34
+ {{tab}}
35
+ <pre>
36
+ &lt;itf-tabs v-model="tab">
37
+ &lt;itf-tab id="Test1">
38
+ asd
39
+ </itf-tab>
40
+ &lt;/itf-tabs>
41
+ </pre>
42
+
43
+ <h3>Example</h3>
44
+
45
+ <itf-tabs v-model="tab">
46
+ <itf-tab id="Test1">
47
+ Content 1
48
+ </itf-tab>
49
+ <itf-tab id="Test2">
50
+ Content 2
51
+ </itf-tab>
52
+ <itf-tab id="Test3">
53
+ Content 3
54
+ </itf-tab>
55
+ <itf-tab id="Test4">
56
+ Content 4
57
+ </itf-tab>
58
+ <itf-tab-content id="Test1">
59
+ asd
60
+ </itf-tab-content>
61
+ <itf-tab-content id="Test2">
62
+ asd123
63
+ </itf-tab-content>
64
+ </itf-tabs>
65
+ </div>`,
66
+ }));
@@ -0,0 +1,105 @@
1
+ @import '../../assets/scss/variables';
2
+ @import '~bootstrap/scss/grid';
3
+ @import '~bootstrap/scss/containers';
4
+
5
+ :root {
6
+ --itf-tabs-active-bg: #fff;
7
+ --itf-tabs-active-color: #222;
8
+ --itf-tabs-active-border-color: #ffb20fc2;
9
+ --itf-tabs-inactive-bg: #fafafa;
10
+ --itf-tabs-inactive-color: #222;
11
+ --itf-tabs-inactive-border-color: rgba(0, 0, 0, .1);
12
+ --itf-tabs-hover-bg: #F4F7F9;
13
+ --itf-tabs-hover-color: #222;
14
+ --itf-tabs-hover-border-color: rgba(0, 0, 0, .5);
15
+ --itf-tabs-tab-padding: 10px;
16
+
17
+ --itf-tabs-content-bg: #fff;
18
+ }
19
+
20
+ .itf-tabs {
21
+ .itf-tabs-panel {
22
+ margin: 0;
23
+ padding: 0;
24
+ overflow: hidden;
25
+ padding-left: calc(var(--itf-tabs-tab-padding) * 2.1);
26
+ display: flex;
27
+ flex-direction: row-reverse;
28
+ justify-content: start;
29
+ flex-wrap: wrap;
30
+
31
+ .itf-tab {
32
+ white-space: nowrap;
33
+ display: block;
34
+ //float: right;
35
+ padding: 10px var(--itf-tabs-tab-padding) 8px;
36
+ margin-right: calc(var(--itf-tabs-tab-padding) * 3);
37
+ z-index: 2;
38
+ position: relative;
39
+ cursor: pointer;
40
+ transition: all 250ms ease;
41
+ border-radius: 8px 8px 0 0;
42
+ background-color: var(--itf-tabs-inactive-bg);
43
+ color: var(--itf-tabs-inactive-color);
44
+ border-top: 1px solid var(--itf-tabs-inactive-border-color);
45
+ tab-index: 0;
46
+ text-decoration: none;
47
+ outline: 0 none;
48
+
49
+ & > span {
50
+ position: relative;
51
+ z-index: 4;
52
+ }
53
+ &:before, &:after {
54
+ display: block;
55
+ content: " ";
56
+ position: absolute;
57
+ top: -1px;
58
+ height: calc(100% + 1px);
59
+ width: calc(var(--itf-tabs-tab-padding) * 5);
60
+ background-color: var(--itf-tabs-inactive-bg);
61
+ color: var(--itf-tabs-inactive-color);
62
+ border-top: 1px solid var(--itf-tabs-inactive-border-color);
63
+ transition: all 250ms ease;
64
+ }
65
+ &:before {
66
+ right: calc(var(--itf-tabs-tab-padding) * -2);
67
+ border-radius: 0 8px 0 0;
68
+ transform: skew(30deg, 0deg);
69
+ box-shadow: rgba(0,0,0,.1) 3px 2px 5px, inset rgba(255,255,255,.09) -1px 0;
70
+ }
71
+ &:after {
72
+ left: calc(var(--itf-tabs-tab-padding) * -2);
73
+ border-radius: 8px 0 0 0;
74
+ //transform: skew(-30deg, 0deg);
75
+ box-shadow: rgba(0,0,0,.1) -3px 2px 5px, inset rgba(255,255,255,.09) 1px 0;
76
+ }
77
+ &:hover, &:hover:before, &:hover:after,
78
+ &:focus, &:focus:before, &:focus:after {
79
+ background-color: var(--itf-tabs-hover-bg);
80
+ color: var(--itf-tabs-hover-color);
81
+ border-top: 1px solid var(--itf-tabs-hover-border-color);
82
+ }
83
+ &.active {
84
+ z-index: 3;
85
+
86
+ &, &:before, &:after {
87
+ background-color: var(--itf-tabs-active-bg);
88
+ color: var(--itf-tabs-active-color);
89
+ border-top: 1px solid var(--itf-tabs-active-border-color);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ .itf-tabs-content {
95
+ .itf-tab-content {
96
+ &.filled {
97
+ background: var(--itf-tabs-content-bg);
98
+ border: 1px solid var(--itf-tabs-inactive-border-color);
99
+ margin-top: -1px;
100
+ border-bottom-left-radius: $border-radius;
101
+ border-bottom-right-radius: $border-radius;
102
+ }
103
+ }
104
+ }
105
+ }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
 
3
3
  <div class="itf-text-field input-group" :class="{ 'with-addon addon-start': prependIcon, 'with-addon addon-end': clearable }">
4
- <div class="addon" v-if="prependIcon">
4
+ <div class="addon" v-if="prependIcon || $slots.addon">
5
5
  <slot name="addon">
6
6
  <itf-icon :name="prependIcon"/>
7
7
  </slot>
@@ -19,6 +19,13 @@
19
19
  :readonly="readonly"
20
20
  @input="onInput($event.target.value)"
21
21
  @keydown="$emit('keydown', $event)"
22
+ @keyup="$emit('keyup', $event)"
23
+ @keypress="$emit('keypress', $event)"
24
+ @blur="$emit('blur', $event)"
25
+ @focus="$emit('focus', $event)"
26
+ :min="min"
27
+ :max="max"
28
+ :step="step"
22
29
  />
23
30
 
24
31
  <div class="addon-end" v-if="clearable && value">
@@ -68,6 +75,9 @@ class itfTextField extends Vue {
68
75
  @Model('input') value;
69
76
  @Prop(String) prependIcon;
70
77
  @Prop(String) placeholder;
78
+ @Prop() step;
79
+ @Prop() min;
80
+ @Prop() max;
71
81
  @Prop(Boolean) clearable;
72
82
  @Prop(Boolean) disabled;
73
83
  @Prop(Boolean) readonly;
@@ -95,5 +105,13 @@ class itfTextField extends Vue {
95
105
  const endText = value.slice(position);
96
106
  this.$emit('input', `${startText}${text}${endText}`);
97
107
  }
108
+
109
+ focus() {
110
+ this.$refs.input.focus();
111
+ }
112
+
113
+ blur() {
114
+ this.$refs.input.blur();
115
+ }
98
116
  }
99
117
  </script>
@@ -18,6 +18,10 @@
18
18
  :value="value"
19
19
  @input="onInput($event.target.value)"
20
20
  @keydown="$emit('keydown', $event)"
21
+ @keyup="$emit('keyup', $event)"
22
+ @keypress="$emit('keypress', $event)"
23
+ @blur="$emit('blur', $event)"
24
+ @focus="$emit('focus', $event)"
21
25
  />
22
26
  </div>
23
27
 
@@ -95,5 +99,13 @@ class itfTextarea extends Vue {
95
99
  const endText = value.slice(position);
96
100
  this.$emit('input', `${startText}${text}${endText}`);
97
101
  }
102
+
103
+ focus() {
104
+ this.$refs.input.focus();
105
+ }
106
+
107
+ blur() {
108
+ this.$refs.input.blur();
109
+ }
98
110
  }
99
111
  </script>
@@ -1,54 +1,19 @@
1
1
  <template>
2
2
 
3
- <div class="eo-tabset breadcrumbs">
3
+ <div class="py-3 px-2">
4
4
 
5
- <ul class="nav nav-pills flex-column mb-auto">
6
- <li class="nav-item">
7
- <a href="#" class="nav-link active" aria-current="page">
8
- <svg class="bi me-2" width="16" height="16"><use xlink:href="#home"></use></svg>
9
- Home
10
- </a>
11
- </li>
12
- <li>
13
- <a href="#" class="nav-link link-dark">
14
- <svg class="bi me-2" width="16" height="16"><use xlink:href="#speedometer2"></use></svg>
15
- Dashboard
16
- </a>
17
- </li>
18
- <li>
19
- <a href="#" class="nav-link link-dark">
20
- <svg class="bi me-2" width="16" height="16"><use xlink:href="#table"></use></svg>
21
- Orders
22
- </a>
23
- </li>
24
- <li>
25
- <a href="#" class="nav-link link-dark">
26
- <svg class="bi me-2" width="16" height="16"><use xlink:href="#grid"></use></svg>
27
- Products
28
- </a>
29
- </li>
30
- <li>
31
- <a href="#" class="nav-link link-dark">
32
- <svg class="bi me-2" width="16" height="16"><use xlink:href="#people-circle"></use></svg>
33
- Customers
34
- </a>
35
- </li>
36
- </ul>
37
-
38
- <ul class="nav nav-pills nav-stacked" data-test="step-sidebar">
5
+ <ul class="nav nav-pills flex-column mb-auto ps-0" data-test="step-sidebar">
39
6
 
40
7
  <li v-for="(step, n) in sidebarItems" :class="{ active: selectedId === step.id, disabled: selectedId !== step.id }"
41
8
  :data-test="'step-sidebar-' + step.id">
42
- <strong v-if="selectedId !== step.id && value.indexOf(step.id) !== -1">
43
- <span aria-hidden="true" class="glyphicon m-0-left" :class="'air-icon-' + step['sidebar-icon']"></span>
9
+ <strong v-if="selectedId !== step.id && value.includes(step.id)" class="nav-link text-success">
10
+ <itf-icon :name="step['sidebar-icon'] || 'check_circle'"></itf-icon>
44
11
  {{step['sidebar-title']}}
45
12
  </strong>
46
- <a href="" v-else @click.prevent="">
47
- <span aria-hidden="true" class="glyphicon m-0-left" :class="'air-icon-' + step['sidebar-icon']"></span>
13
+ <a href="" v-else @click.prevent="" class="nav-link">
14
+ <itf-icon :name="step['sidebar-icon'] || 'circle'"></itf-icon>
48
15
  {{step['sidebar-title']}}
49
16
  </a>
50
- <span aria-hidden="true" class="completed-icon glyphicon air-icon-verified"
51
- :class="{ 'completed': value.indexOf(step.id) !== -1 }"></span>
52
17
  </li>
53
18
  </ul>
54
19
 
@@ -56,50 +21,18 @@
56
21
 
57
22
  </template>
58
23
  <style lang="scss">
59
- .itf-wizard-sidebar {
60
- .eo-tabset.breadcrumbs {
61
- .completed-icon {
62
- position: absolute;
63
- right: 0;
64
- top: 0;
65
- height: 100%;
66
- display: flex;
67
- align-items: center;
68
- //color: $gray-light;
69
-
70
- &.completed {
71
- //color: $brand-primary;
72
- }
73
- }
74
-
75
- li.active > a {
76
- //box-shadow: $drop-shadow-darker;
77
- }
78
-
79
- li > a {
80
- .glyphicon {
81
- margin: 0 10px 0 0;
82
- font-size: 14px;
83
- }
84
- }
85
-
86
- li > strong {
87
- display: block;
88
- padding: 9px 20px;
89
-
90
- .glyphicon {
91
- margin: 0 10px 0 0;
92
- }
93
- }
94
- }
95
- }
24
+ .itf-wizard-sidebar {
25
+ }
96
26
  </style>
97
27
  <script>
98
28
  import { Vue, Component, Prop } from 'vue-property-decorator';
29
+ import itfIcon from '../icon/Icon';
99
30
 
100
31
  export default @Component({
101
32
  name: 'itfWizardSidebar',
102
- components: {},
33
+ components: {
34
+ itfIcon
35
+ },
103
36
  })
104
37
  class itfWizardSidebar extends Vue {
105
38
  @Prop(Array) steps;
@@ -108,19 +108,19 @@ class itfWizard extends Vue {
108
108
  }
109
109
 
110
110
  return createElement('div', { staticClass: 'itf-wizard row gx-0' }, [
111
- isSidebarNeeded ? createElement('div', { staticClass: 'col-md-4 d-none d-md-block itf-wizard-sidebar' }, [
111
+ isSidebarNeeded ? createElement('div', { staticClass: 'col-md-3 d-none d-md-flex' }, [
112
112
  createSidebar({
113
113
  value: (this.value || {}).completed || [],
114
114
  steps: this.stepsNodes,
115
115
  selectedId: this.currentSidebarId,
116
116
  }),
117
117
  ]) : null,
118
- createElement('div', { staticClass: isSidebarNeeded ? 'col-md-8 itf-wizard-step' : 'col-xs-12 itf-wizard-step' }, [node])
118
+ createElement('div', { staticClass: isSidebarNeeded ? 'col-md-9 itf-wizard-step' : 'col-xs-12 itf-wizard-step' }, [node])
119
119
  ]);
120
120
 
121
121
  function createSidebar(props) {
122
122
  return createElement('itf-wizard-sidebar', {
123
- staticClass: '',
123
+ staticClass: 'itf-wizard-sidebar w-100',
124
124
  props,
125
125
  });
126
126
  }