@itfin/components 1.2.96 → 1.2.98

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.2.96",
3
+ "version": "1.2.98",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -26,21 +26,22 @@
26
26
  "@vue/cli-service": "^5.0.1",
27
27
  "@vue/composition-api": "^1.7.1",
28
28
  "air-datepicker": "^3.3.5",
29
- "bootstrap": "=5.2.3",
30
- "core-js": "^3.31.1",
29
+ "bootstrap": "^5.2.3",
30
+ "core-js": "^3.7.0",
31
31
  "debug": "^4.2.0",
32
32
  "intersection-observer": "^0.12.2",
33
33
  "lodash": "^4.17.20",
34
34
  "luxon": "^3.3.0",
35
- "pdfjs-dist": "^3.8.162",
35
+ "pdfjs-dist": "^2.10.377",
36
36
  "tippy.js": "^6.3.2",
37
+ "vue": "^2.6.12",
37
38
  "vue-imask": "^6.6.3",
38
39
  "vue-property-decorator": "^9.1.2",
39
40
  "vue-swatches": "^2.1.1",
40
41
  "vue-virtual-scroller": "^1.1.2"
41
42
  },
42
43
  "devDependencies": {
43
- "@babel/eslint-parser": "^7.22.9",
44
+ "@babel/eslint-parser": "^7.19.1",
44
45
  "@babel/plugin-proposal-numeric-separator": "^7.18.6",
45
46
  "@babel/plugin-syntax-numeric-separator": "^7.10.4",
46
47
  "@storybook/addon-docs": "=6.3.8",
@@ -52,15 +53,14 @@
52
53
  "@vue/eslint-config-airbnb": "^7.0.0",
53
54
  "@vue/test-utils": "^1.1.1",
54
55
  "babel-eslint": "^10.1.0",
55
- "eslint": "^8.45.0",
56
- "eslint-plugin-import": "^2.27.5",
56
+ "eslint": "^8.30.0",
57
+ "eslint-plugin-import": "^2.22.1",
57
58
  "eslint-plugin-prettier": "^4.2.1",
58
- "eslint-plugin-vue": "^9.15.1",
59
+ "eslint-plugin-vue": "^9.9.0",
59
60
  "fibers": "^5.0.0",
60
61
  "marked": "^4.2.5",
61
62
  "sass": "^1.29.0",
62
63
  "sass-loader": "^10.0.5",
63
- "vue": "^2.6.12",
64
64
  "vue-class-component": "^7.2.6",
65
65
  "vue-eslint-parser": "^9.1.0",
66
66
  "vue-template-compiler": "=2.6.14"
@@ -9,10 +9,21 @@ $color-outcome: #b91e1e;
9
9
  --color-outcome: #{$color-outcome};
10
10
  --color-primary: #{$primary};
11
11
  --body-bg: #{$body-bg};
12
+
13
+ .modal-backdrop {
14
+ --bs-backdrop-bg: #{$body-bg};
15
+ --bs-backdrop-opacity: 0.75;
16
+ }
12
17
  }
13
18
 
14
19
  [data-theme="dark"] {
15
20
  --color-primary: #{$dark-primary};
16
21
  --body-bg: #{$dark-body-bg};
22
+ --bs-backdrop-bg: #{$dark-body-bg};
17
23
  --color-primary-hover: #{darken($dark-primary, 10%)};
24
+
25
+ .modal-backdrop {
26
+ --bs-backdrop-opacity: 0.5;
27
+ --bs-backdrop-bg: #{$dark-body-bg};
28
+ }
18
29
  }
@@ -21,6 +21,7 @@
21
21
  :unmask="false"
22
22
  :lazy="!focused"
23
23
  :placeholder="placeholder"
24
+ :disabled="disabled"
24
25
  />
25
26
 
26
27
  <div class="addon-end" v-if="clearable && value">
@@ -85,6 +86,7 @@ class itfDatePicker extends Vue {
85
86
  @Prop({ type: String, default: 'bottom-start' }) placement;
86
87
  @Prop({ type: [String, Date], default: '' }) minDate;
87
88
  @Prop(Boolean) clearable;
89
+ @Prop(Boolean) disabled;
88
90
 
89
91
  focused = false;
90
92
 
@@ -117,6 +119,9 @@ class itfDatePicker extends Vue {
117
119
  }
118
120
 
119
121
  mounted() {
122
+ if (this.disabled) {
123
+ return;
124
+ }
120
125
  // якщо в модалці, то контекст модалки, якщо ні, то аплікейшена
121
126
  const context = this.$el.closest('.itf-append-context') || document.body;
122
127
  this.tooltip = tippy(this.$refs.input.$el, {
@@ -183,10 +188,16 @@ class itfDatePicker extends Vue {
183
188
  }
184
189
 
185
190
  onFocus() {
191
+ if (this.disabled) {
192
+ return;
193
+ }
186
194
  this.focused = true;
187
195
  }
188
196
 
189
197
  onBlur(e) {
198
+ if (this.disabled) {
199
+ return;
200
+ }
190
201
  this.focused = false;
191
202
  this.updateValue(e.target.value, !!e.target.value);
192
203
  }
@@ -19,6 +19,7 @@
19
19
  :mask="Date"
20
20
  :pattern="dateFormat"
21
21
  :blocks="blocks"
22
+ :disabled="disabled"
22
23
  :format="format"
23
24
  :parse="parse"
24
25
  :unmask="false"
@@ -105,6 +106,7 @@ class itfDateRangePicker extends Vue {
105
106
  @Prop({ type: String, default: 'bottom-start' }) placement;
106
107
  @Prop({ type: [String, Date], default: '' }) minDate;
107
108
  @Prop({ type: [String, Date], default: ''}) maxDate;
109
+ @Prop(Boolean) disabled;
108
110
 
109
111
  focused = false;
110
112
 
@@ -3,7 +3,7 @@
3
3
  <div class="itf-dropdown" :class="`drop${placement}`">
4
4
  <div v-if="disabled"><slot name="button">{{label}}</slot></div>
5
5
  <itf-button
6
- v-else
6
+ v-else-if="!text"
7
7
  :class="{ 'dropdown-toggle': toggle }"
8
8
  v-bind="buttonOptions"
9
9
  ref="toggle"
@@ -13,6 +13,9 @@
13
13
  >
14
14
  <slot name="button">{{label}}</slot>
15
15
  </itf-button>
16
+ <div v-else :class="{ 'dropdown-toggle': toggle }" ref="toggle" :id="modalId" data-bs-toggle="dropdown" aria-expanded="false">
17
+ <slot name="button">{{label}}</slot>
18
+ </div>
16
19
  <div
17
20
  class="itf-dropdown__menu dropdown-menu"
18
21
  :class="{'dropdown-menu-end': right, 'shadow': shadow}"
@@ -44,6 +47,8 @@ class itfDropdown extends Vue {
44
47
  @Prop({ type: Boolean }) toggle;
45
48
  @Prop({ type: Boolean }) shadow;
46
49
  @Prop({ type: Boolean }) disabled;
50
+ @Prop({ type: Boolean }) text;
51
+ @Prop({ type: Boolean }) appendToBody;
47
52
  @Prop({ validator: (value) => [true, false, 'inside', 'outside'].includes(value), default: true }) autoclose;
48
53
  @Prop({ type: Object, default: () => ({}) }) buttonOptions;
49
54
 
@@ -80,6 +85,12 @@ class itfDropdown extends Vue {
80
85
  reference: 'toggle',
81
86
  autoClose: this.autoclose
82
87
  });
88
+ let context = document.body;
89
+ if (this.appendToBody && this.$refs.dropdown instanceof Node && this.$refs.dropdown.parentNode) {
90
+ this.$refs.dropdown.parentNode.removeChild(this.$refs.dropdown);
91
+ context.appendChild(this.$refs.dropdown); // should append only to body
92
+ }
93
+
83
94
  this.$el.addEventListener('shown.bs.dropdown', () => {
84
95
  setTimeout(() => {
85
96
  this.$emit('open');
@@ -0,0 +1,112 @@
1
+ <template>
2
+
3
+ <div>
4
+ <div ref="element">
5
+ <slot name="activator" :open="open">
6
+ <div @click.prevent.stop="open">
7
+ <slot :open="open" :hide="hide">
8
+ </slot>
9
+ </div>
10
+ </slot>
11
+ </div>
12
+ </div>
13
+
14
+ </template>
15
+ <style lang="scss">
16
+ .itf-editable-element {
17
+ z-index: 1060;
18
+ position: fixed;
19
+ }
20
+ </style>
21
+ <script>
22
+ import { Vue, Component, Prop, PropSync } from 'vue-property-decorator';
23
+ import itfButton from '../button/Button.vue';
24
+ import itfIcon from '../icon/Icon.vue';
25
+ import FocusTrap from "bootstrap/js/src/util/focustrap";
26
+
27
+ export default @Component({
28
+ name: 'itfEditableElement',
29
+ components: {
30
+ itfButton,
31
+ itfIcon,
32
+ }
33
+ })
34
+ class itfEditableElement extends Vue {
35
+ _backdrop = null;
36
+ _context = null;
37
+ _focusTrap = null;
38
+ _elContainer = null;
39
+ _tempContainer = null;
40
+
41
+ isOpen = false;
42
+
43
+ async mounted() {
44
+ this._context = this.$el.closest('.itf-append-context:not(.modal-content)') || document.body;
45
+
46
+ const { default: Backdrop } = await import('../modal/backdrop');
47
+ this._backdrop = new Backdrop({
48
+ rootElement: this._context,
49
+ isVisible: true,
50
+ isAnimated: true,
51
+ clickCallback: () => {
52
+ this.hide();
53
+ }
54
+ });
55
+ this._focusTrap = new FocusTrap({
56
+ trapElement: this.$el
57
+ });
58
+ }
59
+
60
+ open() {
61
+ if (this.isOpen) {
62
+ return;
63
+ }
64
+ this.isOpen = true;
65
+
66
+ this.$emit('open');
67
+ this._backdrop.show();
68
+ this._focusTrap.activate();
69
+
70
+ this._tempContainer = document.createElement('div');
71
+ this.applyPosition(this._tempContainer, this.$refs.element)
72
+ this._tempContainer.classList.add('itf-editable-element');
73
+ this.$el.removeChild(this.$refs.element);
74
+ this._tempContainer.appendChild(this.$refs.element);
75
+ this._context.appendChild(this._tempContainer);
76
+ }
77
+
78
+ applyPosition(container, el) {
79
+ const box = el.getBoundingClientRect();
80
+ const left = box.left + document.body.scrollLeft;
81
+ const top = box.top + document.body.scrollTop;
82
+ container.style.left = `${left}px`;
83
+ container.style.top = `${top}px`;
84
+ container.style.width = `${box.width}px`;
85
+ container.style.height = `${box.height}px`;
86
+ this.$el.style.width = `${box.width}px`;
87
+ this.$el.style.height = `${box.height}px`;
88
+ }
89
+
90
+ hide() {
91
+ if (!this.isOpen) {
92
+ return;
93
+ }
94
+ this._focusTrap.deactivate();
95
+ this._backdrop.hide(() => {
96
+ this._tempContainer.removeChild(this.$refs.element);
97
+ this.$el.appendChild(this.$refs.element);
98
+ this._context.removeChild(this._tempContainer); // remove the temporary div
99
+
100
+ this.$el.style.width = null;
101
+ this.$el.style.height = null;
102
+ this.isOpen = false;
103
+ this.$emit('hide');
104
+ });
105
+ }
106
+
107
+ beforeDestroy() {
108
+ this._backdrop.dispose();
109
+ this._focusTrap.deactivate();
110
+ }
111
+ }
112
+ </script>
@@ -0,0 +1,53 @@
1
+ import { storiesOf } from '@storybook/vue';
2
+ import itfButton from '../button/Button.vue';
3
+ import itfEditableElement from './EditableElement.vue';
4
+
5
+ storiesOf('Common', module)
6
+ .add('Editable element', () => ({
7
+ components: {
8
+ itfButton,
9
+ itfEditableElement
10
+ },
11
+ data() {
12
+ return {}
13
+ },
14
+ template: `<div>
15
+ <p>You need wrap whole application with this tag</p>
16
+
17
+ <h2>Usage</h2>
18
+
19
+ <pre>
20
+ &lt;itf-table
21
+ :columns="columns"
22
+ :rows="list"
23
+ >
24
+ &lt;template #column.Employee="&#123; item }">&lt;/template>
25
+ &lt;/itf-table>
26
+ </pre>
27
+
28
+ <h3>Example</h3>
29
+
30
+ <itf-editable-element>
31
+ <template v-slot="{ hide }">
32
+
33
+ <div class="card" style="width: 300px">
34
+ <div class="card-body">
35
+ asdadas
36
+
37
+ <a href="#" @click="hide">Close</a>
38
+ </div>
39
+ </div>
40
+ </template>
41
+ </itf-editable-element>
42
+
43
+ <!--itf-table
44
+ :columns="columns"
45
+ :rows="list"
46
+ >
47
+ <template #column.Employee="{ item }">
48
+ {{item.Employee}}
49
+ </template>
50
+ </itf-table-->
51
+
52
+ </div>`,
53
+ }));
@@ -0,0 +1,288 @@
1
+ import Vue from 'vue';
2
+ import { Draggable } from './draggable'
3
+ import DraggableEvent from './event'
4
+
5
+
6
+ Vue.directive("sortable-item", {
7
+ inserted(el, { value }) {
8
+ el.sortableItemPayload = value?.payload;
9
+ el.classList.add(draggableNode.options.draggableClass);
10
+ el.getAttribute("with-handle") && el.classList.add(draggableNode.options.dragHandleClass);
11
+ value?.mirror && (el.dataset.draggableMirror = JSON.stringify(value.mirror));
12
+ },
13
+ update(el, {value}) {
14
+ el.sortableItemPayload = value?.payload;
15
+ el.classList.add(draggableNode.options.draggableClass);
16
+ el.getAttribute("with-handle") && el.classList.add(draggableNode.options.dragHandleClass);
17
+ value?.mirror && (el.dataset.draggableMirror = JSON.stringify(value.mirror))
18
+ }
19
+ });
20
+
21
+
22
+ const SORT_TAG = "sort";
23
+ function someFunc({ source, over, overContainer, children }) {
24
+ const isDelete = !children.length
25
+ , isInContainer = source.parentNode !== overContainer
26
+ , Q = over && !isInContainer;
27
+ if (isDelete) {
28
+ return moveToContainer(source, overContainer)
29
+ }
30
+ return Q ? $(source, over) : isInContainer ? insertBefore(source, over, overContainer) : null
31
+ }
32
+
33
+ function moveToContainer(el, newContainer) {
34
+ const oldContainer = el.parentNode;
35
+ newContainer.appendChild(el);
36
+ return {
37
+ oldContainer,
38
+ newContainer
39
+ };
40
+ }
41
+ function $(el, container) {
42
+ console.info('$')
43
+ const tt = indexOf(el)
44
+ , ot = indexOf(container);
45
+ if (tt < ot) {
46
+ el.parentNode.insertBefore(el, container.nextElementSibling);
47
+ } else {
48
+ el.parentNode.insertBefore(el, container);
49
+ }
50
+ return {
51
+ oldContainer: el.parentNode,
52
+ newContainer: el.parentNode
53
+ };
54
+ }
55
+ function indexOf(A) {
56
+ return Array.prototype.indexOf.call(A.parentNode.children, A)
57
+ }
58
+ function insertBefore(A, H, tt) {
59
+ console.info('insertBefore')
60
+ const ot = A.parentNode;
61
+ return H ? H.parentNode.insertBefore(A, H) : tt.appendChild(A),
62
+ {
63
+ oldContainer: ot,
64
+ newContainer: A.parentNode
65
+ }
66
+ }
67
+ function c(A, H, tt) {
68
+ const ot = [...A.slice(0, H), ...A.slice(H + 1, A.length)];
69
+ return [...ot.slice(0, tt), A[H], ...ot.slice(tt, ot.length)]
70
+ }
71
+
72
+ export
73
+ const Sortable = {
74
+ name: 'Sortable',
75
+ render: function() {
76
+ return this._self._c(this.tag, {
77
+ ref: "sortable",
78
+ tag: "component"
79
+ }, [this._t("default", null, {
80
+ items: this.value
81
+ })], 2)
82
+ },
83
+ props: {
84
+ value: {
85
+ default: ()=>[],
86
+ type: Array
87
+ },
88
+ group: {
89
+ type: String,
90
+ default: "defaultGroup"
91
+ },
92
+ tag: {
93
+ type: String,
94
+ default: "div"
95
+ }
96
+ },
97
+ mounted() {
98
+ Draggable.addContainer(this.$refs.sortable);
99
+ this.$refs.sortable.dataset.group = this.group;
100
+ this.handler = event=>{
101
+ const H = [...this.value];
102
+ H.splice(event.detail.newIndex, 0, event.detail.item),
103
+ this.$emit("input", H),
104
+ this.$emit("receive", {
105
+ ...event.detail,
106
+ newItems: H
107
+ })
108
+ };
109
+
110
+ this.$refs.sortable.addEventListener(SORT_TAG, this.handler);
111
+ Draggable.on("drag:start", this.onDragStart);
112
+ },
113
+ beforeDestroy() {
114
+ this.$refs.sortable.removeEventListener(SORT_TAG, this.handler)
115
+ },
116
+ destroyed() {
117
+ Draggable.removeContainer(this.$refs.sortable),
118
+ Draggable.off("drag:start", this.onDragStart),
119
+ this.destroyMirror()
120
+ },
121
+ methods: {
122
+ stopListening() {
123
+ Draggable
124
+ .off("drag:move", this.dragMove)
125
+ .off("drag:over:container", this.onDragOverContainer)
126
+ .off("drag:out:container", this.onDragOutContainer)
127
+ .off("drag:over", this.onDragOver)
128
+ .off("drag:stop", this.onDragStop)
129
+ .off("mirror:created", this.createMirror)
130
+ .off("mirror:destroy", this.destroyMirror);
131
+ this.destroyMirror();
132
+ },
133
+ createMirror(el) {
134
+ console.info(el, this.dragging)
135
+ if (this.dragging && this.$scopedSlots.mirror) {
136
+ const H = this.$scopedSlots.mirror({
137
+ item: this.source.item
138
+ });
139
+ const tt = Vue.ZP.extend({
140
+ parent: this,
141
+ render() {
142
+ return H
143
+ }
144
+ });
145
+ const Y = new tt().$mount();
146
+ this.mirrorComponent = Y;
147
+ el.mirror.innerHTML = "";
148
+ el.mirror.appendChild(Y.$el);
149
+ }
150
+ },
151
+ destroyMirror() {
152
+ if (this.mirrorComponent) {
153
+ this.mirrorComponent.$destroy();
154
+ this.mirrorComponent = null;
155
+ }
156
+ },
157
+ getDraggableElementsForContainer(el) {
158
+ return [...el.children].filter(item => item !== Draggable.originalSource && item !== Draggable.mirror)
159
+ },
160
+ index(el) {
161
+ return this.getDraggableElementsForContainer(el.parentNode).indexOf(el)
162
+ },
163
+ onDragStart(event) {
164
+ if (event.originalEvent.target.tagName === "INPUT") {
165
+ event.cancel();
166
+ return
167
+ }
168
+ if (event.sourceContainer !== this.$refs.sortable) {
169
+ return;
170
+ }
171
+ Draggable
172
+ .on("drag:move", this.dragMove)
173
+ .on("drag:over:container", this.onDragOverContainer)
174
+ .on("drag:out:container", this.onDragOutContainer)
175
+ .on("drag:over", this.onDragOver)
176
+ .on("drag:stop", this.onDragStop)
177
+ .on("mirror:created", this.createMirror)
178
+ .on("mirror:destroy", this.destroyMirror);
179
+ const oldIndex = this.index(event.source);
180
+ this.$emit("start");
181
+ this.oldIndex = oldIndex;
182
+ this.source = {
183
+ oldIndex,
184
+ item: this.value[oldIndex]
185
+ };
186
+ },
187
+ dragMove(el) {
188
+ Draggable.trigger(new DraggableEvent({
189
+ ...el.data,
190
+ sourceComponent: this.$refs.sortable,
191
+ group: this.group,
192
+ draggablePayload: {
193
+ item: this.value[this.oldIndex],
194
+ ...el.originalSource.sortableItemPayload
195
+ }
196
+ }))
197
+ },
198
+ onDragOverContainer(event) {
199
+ const {source, over, overContainer} = event;
200
+ if (overContainer.dataset.group !== this.group) {
201
+ return;
202
+ }
203
+ const children = this.getDraggableElementsForContainer(overContainer);
204
+ this.destinationContainer = overContainer;
205
+
206
+ console.info('onDragOverContainer', overContainer, children)
207
+ someFunc({
208
+ source,
209
+ over,
210
+ overContainer,
211
+ children
212
+ });
213
+ },
214
+ onDragOutContainer(el) {
215
+ this.destinationContainer = null;
216
+ const overContainer = this.$refs.sortable
217
+ , children = this.getDraggableElementsForContainer(overContainer)
218
+ , over = children[this.source.oldIndex];
219
+ someFunc({
220
+ source: el.source,
221
+ over,
222
+ overContainer,
223
+ children
224
+ })
225
+ },
226
+ onDragOver(A) {
227
+ const { source, over, overContainer } = A;
228
+ if (overContainer.dataset.group !== this.group || over === A.originalSource || over === source) {
229
+ return;
230
+ }
231
+ const Y = this.getDraggableElementsForContainer(overContainer);
232
+ if (Y.includes(over)) {
233
+ someFunc({
234
+ source: source,
235
+ over: over,
236
+ overContainer: overContainer,
237
+ children: Y
238
+ });
239
+ }
240
+ },
241
+ onDragStop(item) {
242
+ this.stopListening();
243
+ if (!this.destinationContainer || this.destinationContainer.dataset.group !== this.group) {
244
+ return;
245
+ }
246
+ const { source } = this;
247
+ source.newIndex = this.index(item.source);
248
+ source.sortableItemPayload = { ...item.originalSource.sortableItemPayload };
249
+ console.info(item.source, item.originalSource)
250
+ item.originalSource.parentNode.insertBefore(item.source, item.originalSource);
251
+ if (item.sourceContainer === this.destinationContainer) {
252
+ this.onSortItems(source);
253
+ } else {
254
+ this.onRemoveItem(source);
255
+ this.onReceiveItem(source, this.destinationContainer);
256
+ }
257
+ this.$emit("stop");
258
+ },
259
+ onSortItems(item) {
260
+ if (item.oldIndex === item.newIndex) {
261
+ return;
262
+ }
263
+ const newItems = c(this.value, item.oldIndex, item.newIndex);
264
+ this.$emit("input", newItems);
265
+ this.$emit("move", {
266
+ newItems,
267
+ ...item
268
+ });
269
+ },
270
+ onReceiveItem(el, H) {
271
+ H.dispatchEvent(new CustomEvent(SORT_TAG,{
272
+ detail: {
273
+ item: el.item,
274
+ newIndex: el.newIndex,
275
+ oldIndex: el.oldIndex,
276
+ sortableItemPayload: el.sortableItemPayload
277
+ }
278
+ }))
279
+ },
280
+ onRemoveItem(el) {
281
+ const H = this.value.filter(tt=>tt !== el.item);
282
+ this.$emit("input", H)
283
+ }
284
+ }
285
+ };
286
+
287
+
288
+ Vue.component("Sortable", Sortable)