@itfin/components 1.3.86 → 1.3.87

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.
@@ -0,0 +1,74 @@
1
+ // Unsharp text [#166](https://github.com/NightCatSama/vue-slider-component/issues/166)
2
+ export const roundToDPR = (function () {
3
+ const r = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1
4
+ return value => Math.round(value * r) / r
5
+ })()
6
+
7
+ export const isMobile = (() => {
8
+ const userAgentInfo = navigator.userAgent.toLowerCase()
9
+ const agents = ["Android", "iPhone",
10
+ "SymbianOS", "Windows Phone",
11
+ "iPad", "iPod"]
12
+ let flag = false
13
+ for (let v = 0; v < agents.length; v++) {
14
+ if (userAgentInfo.indexOf(agents[v].toLowerCase()) > 0) {
15
+ flag = true
16
+ break
17
+ }
18
+ }
19
+ return flag
20
+ })()
21
+
22
+ export function isArray(input) {
23
+ if (Array.prototype.isArray) {
24
+ return Array.isArray(input)
25
+ }
26
+ return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'
27
+ }
28
+
29
+ export function isDiff(a, b) {
30
+ if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) {
31
+ return true
32
+ } else if (isArray(a) && a.length === b.length) {
33
+ return a.some((v, i) => v !== b[i])
34
+ }
35
+ return a !== b
36
+ }
37
+
38
+ let elementStyle = document.createElement('div').style
39
+ let vendor = (() => {
40
+ let transformNames = {
41
+ webkit: 'webkitTransform',
42
+ Moz: 'MozTransform',
43
+ O: 'OTransform',
44
+ ms: 'msTransform',
45
+ standard: 'transform'
46
+ }
47
+ for (let key in transformNames) {
48
+ if (elementStyle[transformNames[key]] !== undefined) {
49
+ return key
50
+ }
51
+ }
52
+ return false
53
+ })()
54
+
55
+ export function prefixStyle(style) {
56
+ if (vendor === false) {
57
+ return false
58
+ }
59
+ if (vendor === 'standard') {
60
+ if (style === 'transitionEnd') {
61
+ return 'transitionend'
62
+ }
63
+ return style
64
+ }
65
+ return vendor + style.charAt(0).toUpperCase() + style.substr(1)
66
+ }
67
+
68
+ export function addEvent(el, type, fn, capture) {
69
+ el.addEventListener(type, fn, {passive: false, capture: !!capture})
70
+ }
71
+
72
+ export function removeEvent(el, type, fn, capture) {
73
+ el.removeEventListener(type, fn, {passive: false, capture: !!capture})
74
+ }
@@ -11,7 +11,7 @@
11
11
  ref="input"
12
12
  autocomplete="off"
13
13
  :placeholder="placeholder"
14
- :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess() }"
14
+ :class="{ 'is-invalid': isInvalid(), 'is-valid': isSuccess(), 'form-control-sm': small }"
15
15
  class="itf-text-field__input form-control"
16
16
  :type="type"
17
17
  :value="value"
@@ -67,6 +67,7 @@ class itfTextField extends Vue {
67
67
  @Prop(Boolean) clearable;
68
68
  @Prop(Boolean) disabled;
69
69
  @Prop(Boolean) readonly;
70
+ @Prop(Boolean) small;
70
71
  @Prop({ type: String, default: 'text' }) type;
71
72
  @Prop({ type: [Number, String], default: 0 }) delayInput;
72
73
 
package/src/locales/en.js CHANGED
@@ -125,5 +125,15 @@ module.exports = {
125
125
  itemsPerPage: 'Items per page',
126
126
  previous: 'Previous',
127
127
  next: 'Next'
128
+ },
129
+ filter: {
130
+ search: 'Search',
131
+ deselectAll: 'Deselect all',
132
+ selectAll: 'Select all',
133
+ showSelected: 'Show selected',
134
+ showAll: 'Show all',
135
+ noResults: 'No results',
136
+ showMore: 'show all ({count})',
137
+ hideMore: 'hide',
128
138
  }
129
139
  };
package/src/locales/uk.js CHANGED
@@ -125,5 +125,15 @@ module.exports = {
125
125
  calculateCountNotEmpty: 'Кількість не порожніх',
126
126
  calculatePercentEmpty: 'Відсоток порожніх',
127
127
  calculatePercentNotEmpty: 'Відсоток не порожніх',
128
+ },
129
+ filter: {
130
+ search: 'Пошук',
131
+ deselectAll: 'Скасувати вибір',
132
+ selectAll: 'Вибрати все',
133
+ showSelected: 'Показати вибрані',
134
+ showAll: 'Показати все',
135
+ noResults: 'Немає результатів',
136
+ showMore: 'показати всі ({count})',
137
+ hideMore: 'сховати',
128
138
  }
129
139
  };
@@ -1,196 +0,0 @@
1
- <template>
2
-
3
- <div class="b-condition-group-input w-100 pt-1 pb-2 px-2">
4
- <div class="d-flex align-items-start">
5
- <div v-if="value.conditions.length > 1" class="condition-group-type">
6
- <itf-select
7
- :value="value.link"
8
- :reduce="(item) => item.value"
9
- :options="[{ label: 'AND', value: 'and' }, { label: 'OR', value: 'or' }]"
10
- @input="onLinkUpdate"
11
- />
12
- </div>
13
- <div class="flex-1 pt-1">
14
- <div v-for="(filter, n) in value.conditions" :key="n" :class="{'tree-node': value.conditions.length > 1}">
15
- <div class="d-flex align-items-start">
16
- <div class="flex-grow-1 py-2 me-2">
17
- <itf-condition-group
18
- v-if="filter.link"
19
- :level="level + 1"
20
- class="rounded"
21
- :value="filter"
22
- @input="onFilterUpdate(n, $event)"
23
- :class="{'bg-group1': level === 0, 'bg-group2': level === 1}"
24
- />
25
- <filter-input
26
- v-else
27
- :value="filter"
28
- @input="onFilterUpdate(n, $event)"
29
- />
30
- </div>
31
- <div class="pt-2 mt-1">
32
- <itf-button small icon @click="removeField(n)">
33
- <itf-icon name="trash" />
34
- </itf-button>
35
- </div>
36
- </div>
37
- </div>
38
- </div>
39
- </div>
40
-
41
- <itf-button small secondary @click="addCondition">
42
- <itf-icon name="plus" />
43
- Add condition
44
- </itf-button>
45
- <itf-button v-if="level < maxDepth" small secondary @click="addConditionGroup">
46
- <itf-icon name="plus" />
47
- Add group
48
- </itf-button>
49
- </div>
50
-
51
- </template>
52
- <style>
53
- .b-condition-group-input {
54
- --b-filterGroupBorder: rgba(0,0,0,.05);
55
- --b-filterGroup1Bg: #f9fafb;
56
- --b-filterGroup2Bg: #f1f2f4;
57
-
58
- [data-theme="dark"] & {
59
- --b-filterGroup1Bg: #303436;
60
- --b-filterGroup2Bg: #3a3e41;
61
- --b-filterGroupBorder: hsla(0,0%,100%,.03);
62
- }
63
- }
64
- .bg-group1 {
65
- background: var(--b-filterGroup1Bg);
66
- border: 1px solid var(--b-filterGroupBorder);
67
- }
68
- .bg-group2 {
69
- background: var(--b-filterGroup2Bg);
70
- border: 1px solid var(--b-filterGroupBorder);
71
- }
72
- .condition-group-type {
73
- width: 85px;
74
- z-index: 10;
75
- margin-right: 1rem;
76
- margin-top: .5rem;
77
- margin-bottom: .5rem;
78
- position: relative;
79
- }
80
- .flex-1 {
81
- flex: 1 1 0%;
82
- }
83
- .tree-node {
84
- --h-line-position: 1.5rem;
85
- --line-color: #D2D5DF;
86
- --line-width: 1px;
87
- --line-radius: 10px;
88
- --line-connection-position: -3.5rem;
89
- --node-space: .5rem;
90
- margin-bottom: var(--node-space);
91
- position: relative;
92
-
93
- &:first-child:before {
94
- border-left: solid var(--line-width) var(--line-color);
95
- top: var(--h-line-position);
96
- height: calc(100% - var(--h-line-position) + var(--node-space));
97
- width: var(--line-radius);
98
- }
99
- &:before {
100
- display: inline-block;
101
- border-left: solid var(--line-width) var(--line-color);
102
- content: "";
103
- height: calc(100% + var(--node-space));
104
- position: absolute;
105
- left: var(--line-connection-position);
106
- }
107
- &:first-child:after {
108
- height: var(--line-radius);
109
- }
110
- &:after {
111
- display: inline-block;
112
- border-top: solid var(--line-width) var(--line-color);
113
- content: "";
114
- width: calc(var(--line-connection-position)* -1);
115
- position: absolute;
116
- top: var(--h-line-position);
117
- left: var(--line-connection-position);
118
- }
119
- &:last-child:before {
120
- border-color: var(--line-color);
121
- top: 0%;
122
- height: var(--h-line-position);
123
- width: var(--line-radius);
124
- border-bottom-left-radius: var(--line-radius);
125
- }
126
- &:last-child:after {
127
- border-top: none;
128
- border-bottom: solid var(--line-width) var(--line-color);
129
- border-left: solid var(--line-width) var(--line-color);
130
- height: var(--line-radius);
131
- border-bottom-left-radius: var(--line-radius);
132
- top: calc(var(--h-line-position) - var(--line-radius));
133
- }
134
- }
135
- </style>
136
- <script>
137
- import { Component, Prop, Model, Vue, Watch } from 'vue-property-decorator';
138
- import itfButton from '@itfin/components/src/components/button/Button.vue';
139
- import itfTextField from '@itfin/components/src/components/text-field/TextField.vue';
140
- import itfDropdown from '@itfin/components/src/components/dropdown/Dropdown.vue';
141
- import itfSelect from '@itfin/components/src/components/select/Select.vue';
142
- import itfIcon from '@itfin/components/src/components/icon/Icon.vue';
143
- import FilterInput from '~/components/Panels/FilterInput.vue';
144
-
145
- export default @Component({
146
- name: 'itfConditionGroup',
147
- components: {
148
- itfTextField,
149
- itfSelect,
150
- itfDropdown,
151
- itfButton,
152
- itfIcon,
153
- FilterInput
154
- }
155
- })
156
- class ConditionGroup extends Vue {
157
- @Model('input', { default: () => ({ link: 'and', conditions: [] }) }) value;
158
- @Prop({ default: 0 }) level;
159
- @Prop({ default: 1 }) maxDepth;
160
-
161
- onLinkUpdate(link) {
162
- this.$emit('input', { ...this.value, link });
163
- }
164
-
165
- onFilterUpdate(index, filter) {
166
- this.applyValue((conditions) => {
167
- conditions[index] = filter;
168
- });
169
- }
170
-
171
- applyValue(func) {
172
- const newValue = { ...this.value, conditions: [...this.value.conditions] };
173
- func(newValue.conditions);
174
- this.$emit('input', newValue);
175
- }
176
-
177
- addCondition() {
178
- this.applyValue((conditions) => {
179
- conditions.push({ operator: 'is', values: [] });
180
- });
181
- }
182
-
183
- addConditionGroup() {
184
- this.applyValue((conditions) => {
185
- conditions.push({ link: 'and', conditions: [] });
186
- });
187
- // this.filters.push({ type: 'group' });
188
- }
189
-
190
- removeField(index) {
191
- this.applyValue((conditions) => {
192
- conditions.splice(index, 1);
193
- });
194
- }
195
- }
196
- </script>
@@ -1,138 +0,0 @@
1
- <template>
2
- <itf-form class="facets-panel" :class="{ horizontal }">
3
- <div v-for="(item, n) of filters" :key="n" class="mb-3">
4
- <template v-if="item.facet && ((facets[item.facet] && !horizontal) || horizontal)">
5
- <div><strong>{{ item.label }}</strong></div>
6
- <div class="facets-container">
7
- <div v-if="horizontal" class="facets-search">
8
- <input type="text" :placeholder="$t('search')" :value="query[item.facet]" @input="(e) => setQuery(e, item.facet)">
9
- </div>
10
- <div v-if="facets[item.facet]" class="facets-container__scroll">
11
- <facet-item
12
- :query="query[item.facet]"
13
- :facet="facets[item.facet]"
14
- :item="item"
15
- :value="filter"
16
- :show-all="horizontal"
17
- @input="setFilterValue"
18
- />
19
- </div>
20
- </div>
21
- </template>
22
- <filter-item
23
- v-else-if="!item.facet"
24
- :type="item.type"
25
- :filter="item"
26
- :value="filter"
27
- :prepend-icon="item.id && item.id.includes('custom_') ? null : 'search'"
28
- :labels="labels"
29
- :loading-labels="loadingLabels"
30
- @input="setFilterValue"
31
- />
32
- </div>
33
-
34
- <div v-for="(facet, n) of customFields" :key="`cf_${n}`" class="mb-3">
35
- <div><strong>{{ facet.Name }}</strong></div>
36
- <div class="facets-container">
37
- <facet-item
38
- :facet="facet"
39
- :item="{ id: facet.Id, title: facet.Name }"
40
- :value="filter"
41
- :show-all="horizontal"
42
- @input="setFilterValue"
43
- />
44
- </div>
45
- </div>
46
- </itf-form>
47
- </template>
48
- <style lang="scss" scoped>
49
- .facets-panel {
50
- &.horizontal {
51
- display: grid;
52
- grid-template-columns: repeat(7, clamp(150px, 13.75%, 15%));
53
- grid-gap: .5rem;
54
-
55
- .facets-container {
56
- border: 1px solid var(--bs-border-color);
57
- max-height: 100%;
58
- min-height: 90%;
59
-
60
- .facets-search {
61
- border-bottom: 1px solid var(--bs-border-color);
62
-
63
- input {
64
- padding: .5rem;
65
- border: 0;
66
- width: 100%;
67
- }
68
- }
69
- &::v-deep .facet-bar {
70
- display: none;
71
- }
72
- &__scroll {
73
- max-height: 200px;
74
- overflow: auto;
75
- }
76
- }
77
- }
78
- }
79
- </style>
80
- <script>
81
- import { Vue, Prop, Model, Component } from 'vue-property-decorator';
82
- import itfButton from '@itfin/components/src/components/button/Button';
83
- import itfForm from '@itfin/components/src/components/form/Form';
84
- import FilterItem from './FilterItem.vue';
85
- import FacetItem from './FacetItem.vue';
86
-
87
- export default @Component({
88
- name: 'FacetFilters',
89
- components: {
90
- itfForm,
91
- itfButton,
92
- FilterItem,
93
- FacetItem
94
- }
95
- })
96
- class FacetFilters extends Vue {
97
- @Model('input') value;
98
- @Prop(String) type;
99
- @Prop() filter;
100
- @Prop() labels;
101
- @Prop() loadingLabels;
102
- @Prop() facets;
103
- @Prop(Boolean) horizontal;
104
- @Prop(Array) filters;
105
-
106
- query = {};
107
-
108
- setQuery(e, facet) {
109
- this.query = {
110
- ...this.query,
111
- [facet]: e.target.value
112
- };
113
- }
114
-
115
- get customFields() {
116
- if (!this.facets) {
117
- return [];
118
- }
119
- const fields = [];
120
- for (const key in this.facets) {
121
- const item = this.facets[key];
122
- if (item.IsCustomField) {
123
- fields.push({
124
- ...item,
125
- Id: key
126
- });
127
- }
128
- }
129
- return fields;
130
- }
131
-
132
- setFilterValue (newFilter = {}) {
133
- const filter = { ...newFilter };
134
-
135
- this.$emit('update:filter', filter);
136
- }
137
- }
138
- </script>
@@ -1,162 +0,0 @@
1
- <template>
2
- <div>
3
- <button v-for="(val, n) of mappedValues" :key="n" class="facet-item" :class="{'active': val.isSelected}" @click="onFilterClick(val)">
4
- <span class="facet-name text-dark">{{ val.Text }}</span>
5
- <span class="facet-stat text-muted">
6
- {{ val.Count }}
7
- <span class="facet-bar"><span :style="{'--bar-width': `${getPercent(val)}%`}" class="facet-bar-progress" /></span>
8
- </span>
9
- </button>
10
-
11
- <itf-button v-if="hasMore" small block @click="toggleMore">
12
- <span v-if="showMore">{{ $t('common.hideMore', { count: facet.Values.length }) }}</span>
13
- <span v-else>{{ $t('common.showMore', { count: facet.Values.length }) }}</span>
14
- </itf-button>
15
- </div>
16
- </template>
17
- <style lang="scss" scoped>
18
- .facet-item {
19
- background: transparent;
20
- cursor: pointer;
21
- display: inline-flex;
22
- -webkit-box-align: center;
23
- align-items: center;
24
- -webkit-box-pack: justify;
25
- justify-content: space-between;
26
- position: relative;
27
- box-sizing: border-box;
28
- height: 1.75rem;
29
- width: 100%;
30
- padding-left: 0.25rem;
31
- padding-right: 0.25rem;
32
- font-size: 0.875rem;
33
- line-height: 1.25rem;
34
- font-weight: 400;
35
- white-space: normal;
36
- user-select: none;
37
- border-radius: 0.25rem;
38
- border-width: 1px;
39
- border-style: solid;
40
- border-color: transparent;
41
- border-image: initial;
42
- transition: none 0s ease 0s;
43
- &.active {
44
- background-color: rgba(var(--bs-primary-rgb), 25%);
45
- }
46
- &:hover, &:focus, &.active {
47
- border-color: var(--bs-primary);
48
- }
49
- .facet-name {
50
- text-align: left;
51
- white-space: nowrap;
52
- overflow: hidden;
53
- text-overflow: ellipsis;
54
- }
55
- .facet-stat {
56
- display: flex;
57
- -webkit-box-align: center;
58
- align-items: center;
59
- margin-left: 0.25rem;
60
- }
61
- .facet-bar {
62
- display: inline-block;
63
- margin-left: 0.5rem;
64
- width: 60px;
65
- }
66
- .facet-bar-progress {
67
- display: block;
68
- width: var(--bar-width);
69
- min-width: 5px;
70
- height: 10px;
71
- background-color: rgb(197, 205, 223);
72
- transition: width 0.3s ease 0s;
73
- }
74
- }
75
- </style>
76
- <script>
77
- import { Vue, Prop, Model, Component } from 'vue-property-decorator';
78
- import itfButton from '@itfin/components/src/components/button/Button';
79
- import { DateTime } from 'luxon';
80
-
81
- export default @Component({
82
- name: 'FacetItem',
83
- components: {
84
- itfButton
85
- }
86
- })
87
- class FacetItem extends Vue {
88
- @Model('input') value;
89
- @Prop() facet;
90
- @Prop() item;
91
- @Prop() query;
92
- @Prop(Boolean) showAll;
93
-
94
- showMore = false;
95
-
96
- toggleMore() {
97
- this.showMore = !this.showMore;
98
- }
99
-
100
- get hasMore() {
101
- if (this.showAll) {
102
- return false;
103
- }
104
- return this.facet.Values.length > 5;
105
- }
106
-
107
- get mappedValues() {
108
- const { id, items = [], multiple } = this.item || {};
109
- let list = this.facet.Values.map(val => {
110
- const item = items.find(i => `${i.value}` === `${val.Value}`);
111
- const isSelected = multiple
112
- ? Array.isArray(this.value[id]) && this.value[id].map(String).includes(`${val.Value}`)
113
- : `${this.value[id]}` === `${val.Value}`;
114
-
115
- if (item) {
116
- // якщо знайдено item, ЗАТРЕ Text в val і замінить його на item.title
117
- return { ...val, Text: item.title, isSelected };
118
- }
119
- return { ...val, isSelected };
120
- });
121
-
122
- list = list.map(val => val.Text ? { ...val, Text: this.getFormattedItemTitle(val.Text) } : val);
123
-
124
- if (this.query) {
125
- list = list.filter((val) => val.Text.toLowerCase().includes(this.query.toLowerCase()));
126
- }
127
- if (!this.showMore && !this.showAll) {
128
- return list.slice(0, 5);
129
- }
130
- return list;
131
- }
132
-
133
- getFormattedItemTitle(title) {
134
- return this.isValueDateInStandartFormat(title) ? DateTime.fromFormat(title, 'yyyy-MM-dd', { zone: 'utc' }).toFormat('MM/dd/yyyy') : title;
135
- }
136
-
137
- isValueDateInStandartFormat(value) {
138
- return typeof value === 'string' ? DateTime.fromFormat(value, 'yyyy-MM-dd', { zone: 'utc' }).isValid : false;
139
- }
140
-
141
- getPercent(item) {
142
- return this.facet.Total ? Math.round((item.Count / this.facet.Total) * 100) : 0;
143
- }
144
-
145
- onFilterClick(val) {
146
- const newVal = { ...this.value };
147
- if (this.item.multiple) {
148
- newVal[this.item.id] = [...Array.isArray(newVal[this.item.id]) ? newVal[this.item.id] : []].map((s) => s.toString());
149
- if (newVal[this.item.id].includes(`${val.Value}`)) {
150
- newVal[this.item.id].splice(newVal[this.item.id].indexOf(`${val.Value}`), 1);
151
- } else {
152
- newVal[this.item.id].push(`${val.Value}`);
153
- }
154
- } else if (`${newVal[this.item.id]}` === `${val.Value}`) {
155
- newVal[this.item.id] = null;
156
- } else {
157
- newVal[this.item.id] = val.Value;
158
- }
159
- this.$emit('input', newVal);
160
- }
161
- }
162
- </script>
@@ -1,64 +0,0 @@
1
- <template>
2
-
3
- <div class="d-flex">
4
- <div class="w-25">
5
- <itf-select :options="fields" />
6
- </div>
7
- <div class="w-25 px-1">
8
- <itf-select
9
- :reduce="(item) => item.id"
10
- :get-option-label="(item) => item.title"
11
- :options="conditions"
12
- >
13
- <template #selected-option="{ option }">
14
- <span class="sign-box"> {{option.title}}</span>
15
- </template>
16
- <template #option="{ option }">
17
- {{option.title}}
18
- </template>
19
- </itf-select>
20
- </div>
21
- <div class="w-50">
22
- <itf-text-field />
23
- </div>
24
- </div>
25
-
26
- </template>
27
- <style>
28
- </style>
29
- <script>
30
- import { Component, Prop, Model, Vue, Watch } from 'vue-property-decorator';
31
- import itfButton from '@itfin/components/src/components/button/Button.vue';
32
- import itfTextField from '@itfin/components/src/components/text-field/TextField.vue';
33
- import itfSelect from '@itfin/components/src/components/select/Select.vue';
34
- import itfDropdown from '@itfin/components/src/components/dropdown/Dropdown.vue';
35
- import itfIcon from '@itfin/components/src/components/icon/Icon.vue';
36
-
37
- const conditions = [
38
- { id: 'eq', title: 'equal', sign: '=' },
39
- { id: 'notEq', title: 'not equal', sign: '≠' },
40
- { id: 'contains', title: 'contains', sign: '∋' },
41
- { id: 'noContains', title: 'not contains', sign: '∌' },
42
- { id: 'startsWith', title: 'starts with', sign: 'A…' },
43
- { id: 'endsWith', title: 'ends with', sign: '…Z' },
44
- // { id: 'exists', title: 'exists', sign: '∃' }
45
- ];
46
-
47
- export default @Component({
48
- name: 'itfFilterInput',
49
- components: {
50
- itfTextField,
51
- itfSelect,
52
- itfDropdown,
53
- itfButton,
54
- itfIcon
55
- }
56
- })
57
- class FilterInput extends Vue {
58
- @Prop() fields;
59
-
60
- get conditions() {
61
- return conditions;
62
- }
63
- }
64
- </script>