@itfin/components 1.2.87 → 1.2.89

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.87",
3
+ "version": "1.2.89",
4
4
  "author": "Vitalii Savchuk <esvit666@gmail.com>",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -20,6 +20,7 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "@egjs/vue-flicking": "^4.10.4",
23
+ "@mdi/js": "^7.2.96",
23
24
  "@popperjs/core": "^2.11.8",
24
25
  "@vue/cli-service": "^5.0.1",
25
26
  "@vue/composition-api": "^1.7.1",
@@ -0,0 +1,124 @@
1
+ // 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
2
+ @import "~bootstrap/scss/functions.scss";
3
+
4
+ // 2. Include any default variable overrides here
5
+ $font-family-base: 'Fira Sans', sans-serif;
6
+ $headings-font-family: 'Fira Sans', sans-serif;
7
+ $font-family-monospace: "Fira Mono", "Courier New", monospace;
8
+
9
+ $headings-font-size-base: 1rem;
10
+ $h1-font-size: $headings-font-size-base * 2;
11
+ $h2-font-size: $headings-font-size-base * 1.75;
12
+ $h3-font-size: $headings-font-size-base * 1.5;
13
+ $h4-font-size: $headings-font-size-base * 1.25;
14
+ $h5-font-size: $headings-font-size-base * 1.125;
15
+ $h6-font-size: $headings-font-size-base;
16
+
17
+ $zindex-toaster: 1071 !default;
18
+
19
+ $border-radius: .5rem;
20
+ $border-radius-sm: .375rem;
21
+ $border-radius-lg: .75rem;
22
+
23
+ $project-tnm: #311b92;
24
+ $project-fixed: #388E3C;
25
+ $project-nonprofit: #D32F2F;
26
+
27
+ $info: #5981c0;
28
+ $success: #10834e;
29
+ $warning: #cda277;
30
+ $danger: #cb4839;
31
+
32
+ $primary: #0B314F;
33
+ $link-color: #0B314F;
34
+ $input-btn-focus-width: .125rem;
35
+
36
+ $input-bg: #f3f3f3;
37
+ $input-border-color: rgba(#000, .08);
38
+ $input-border-radius: 10px;
39
+ $input-font-size: 0.875rem;
40
+ $input-font-family: "Fira Mono", "Courier New", monospace;
41
+
42
+ $input-focus-bg: #fff;
43
+ $input-focus-border-color: #fff;
44
+
45
+ $form-label-margin-bottom: .1rem;
46
+ $input-focus-border: rgb(11 49 79 / 25%);
47
+
48
+ $form-check-input-border: 1px solid rgba(#000, .08);
49
+ $form-switch-focus-color: tint-color($primary, 50%);
50
+
51
+ $modal-backdrop-bg: #999;
52
+ $modal-backdrop-opacity: .25;
53
+ $modal-content-border-radius: 0;
54
+
55
+ $secondary: #dfe1e6;
56
+
57
+ // popover
58
+ $popover-border-width: 2px;
59
+
60
+ // dropdown
61
+
62
+ // Dark theme
63
+ $dark-body-bg: #2e3136;
64
+ $dark-border-color: #4a4a4a;
65
+ $dark-body-color: #ddd;
66
+ $dark-input-bg: #383b41;
67
+
68
+ $dark-primary: #FFCC00;
69
+ $dark-link-color: #ead272;
70
+ $dark-secondary: #464a53;
71
+ $dark-input-focus-border-color: #3d3d3d;
72
+ $dark-btn-primary-color: $dark-primary;
73
+ $dark-btn-secondary-color: $dark-secondary;
74
+ $dark-input-box-shadow: #3d3d3d;
75
+ $dark-input-focus-border: rgb(244 206 176 / 25%);
76
+
77
+ $pagination-border-width: 0;
78
+
79
+ // 3. Include remainder of required Bootstrap stylesheets
80
+ @import "~bootstrap/scss/variables.scss";
81
+ @import "~bootstrap/scss/mixins.scss";
82
+ @import "~bootstrap/scss/maps.scss";
83
+ @import "~bootstrap/scss/utilities.scss";
84
+
85
+ $custom-colors: (
86
+ "white": #fff,
87
+ "black": #1e1e1e
88
+ );
89
+ $theme-colors: map-merge($theme-colors, $custom-colors);
90
+
91
+ // Text colors
92
+ $all-colors: map-merge-multiple($theme-colors, $custom-colors, $blues, $indigos, $purples, $pinks, $reds, $oranges, $yellows, $greens, $teals, $cyans);
93
+ $utilities: map-merge(
94
+ $utilities,
95
+ (
96
+ "color": map-merge(
97
+ map-get($utilities, "color"),
98
+ (
99
+ values: map-merge(
100
+ map-get(map-get($utilities, "color"), "values"),
101
+ (
102
+ $all-colors
103
+ ),
104
+ ),
105
+ ),
106
+ ),
107
+ )
108
+ );
109
+ @import "~bootstrap/scss/utilities/api";
110
+
111
+ // Fonts
112
+ $baseFontSize: 16px;
113
+ $hintFontSize: 12px;
114
+
115
+ // Border radius
116
+ $borderRadiusM: 4px;
117
+ $borderRadiusL: 8px;
118
+
119
+ // Colors
120
+ $primaryColor: #0B314F;
121
+ $primaryColor15: #DAE0E5;
122
+ $baseText: #1e1e1e;
123
+ $gray5: #F0F0F1;
124
+ $gray50: #787885;
@@ -1,5 +1,7 @@
1
1
  @import '../variables';
2
2
 
3
3
  .itf-icon {
4
- display: inline-block;
4
+ display: inline-flex;
5
+ align-items: center;
6
+ vertical-align: middle;
5
7
  }
@@ -47,3 +47,14 @@
47
47
  .color-outcome {
48
48
  color: #b91e1e !important;
49
49
  }
50
+
51
+ kbd {
52
+ background-color: rgba(0, 0, 0, .15) !important;
53
+ color: #000 !important;
54
+ font-size: .75rem !important;
55
+
56
+ [data-theme="dark"] &, .btn-primary & {
57
+ background-color: rgba(255, 255, 255, .25) !important;
58
+ color: #fff !important;
59
+ }
60
+ }
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <div class="itf-inline-edit">
3
+ <div ref="input" class="input" :role="editable ? 'button' : 'textbox'" tabindex="1" @keydown.space="onEnter" @keydown.enter="onEnter" @focus="$emit('focus')" @blur="$emit('blur')">
4
+ <span v-if="value">{{ value | formatDate }}</span>
5
+ <span v-else class="text-muted">Empty</span>
6
+ </div>
7
+
8
+ <div style="display: none">
9
+ <div ref="dropdown" class="itf-datepicker__dropdown">
10
+ <itf-date-picker-inline
11
+ :value="value"
12
+ start-view="days"
13
+ display-format="MM/dd/yyyy"
14
+ value-format="yyyy-MM-dd"
15
+ @input="selectInlineDate"
16
+ ></itf-date-picker-inline>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </template>
21
+ <style lang="scss" scoped>
22
+ .itf-inline-edit .input {
23
+ border: 0 none;
24
+ margin: 0;
25
+ outline: 0;
26
+ resize: none;
27
+ background: transparent;
28
+ }
29
+ </style>
30
+ <script>
31
+ import {Vue, Component, Model, Watch, Prop} from 'vue-property-decorator';
32
+ import itfTextField from '../../text-field/TextField.vue';
33
+ import itfDatePickerInline from '../../datepicker/DatePickerInline.vue';
34
+ import tippy from 'tippy.js';
35
+ import { formatDate } from '../../../helpers/formatters';
36
+
37
+ export default @Component({
38
+ name: 'itfCustomizeInlineDate',
39
+ components: {
40
+ itfTextField,
41
+ itfDatePickerInline
42
+ },
43
+ filters: {
44
+ formatDate
45
+ }
46
+ })
47
+
48
+ class itfCustomizeInlineText extends Vue {
49
+ @Model('input') value;
50
+ @Prop({ type: String, default: 'bottom-start' }) placement;
51
+ @Prop({ type: Boolean, default: false }) editable;
52
+
53
+ tooltip = null;
54
+
55
+ mounted() {
56
+ if (!this.editable) {
57
+ return;
58
+ }
59
+ const context = this.$el.closest('.itf-append-context') || document.body;
60
+ this.tooltip = tippy(this.$refs.input, {
61
+ interactiveBorder: 30,
62
+ interactiveDebounce: 75,
63
+ animation: 'scale',
64
+ arrow: true,
65
+ content: this.$refs.dropdown,
66
+ allowHTML: true,
67
+ trigger: 'click',
68
+ interactive: true,
69
+ placement: this.placement,
70
+ appendTo: context
71
+ });
72
+ }
73
+
74
+ selectInlineDate(date) {
75
+ this.$emit('input', date);
76
+ this.tooltip.hide();
77
+ this.isFocused = false;
78
+ }
79
+
80
+ onEnter() {
81
+ this.tooltip.show();
82
+ }
83
+ }
84
+ </script>
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <div class="itf-inline-edit">
3
+ <itf-select
4
+ ref="input"
5
+ class="input"
6
+ role="button"
7
+ multiple
8
+ :placeholder="focused ? 'Search for an option...' : 'Empty'"
9
+ :tabindex="1"
10
+ :disabled="!editable"
11
+ :options="field && field.Options"
12
+ :reduce="option => option.Name"
13
+ :get-option-label="option => option.Name"
14
+ @search:focus="$emit('focus')"
15
+ @search:blur="$emit('blur')">
16
+ <template #option="{ option }">
17
+ <div>{{ option.Name }}</div>
18
+ </template>
19
+ </itf-select>
20
+ </div>
21
+ </template>
22
+ <style lang="scss">
23
+ .itf-inline-edit .itf-select .form-control {
24
+ border: 0 none;
25
+ margin: 0;
26
+ outline: 0;
27
+ resize: none;
28
+ min-height: auto;
29
+ padding: 0.1rem 0;
30
+ font-family: var(--bs-body-font-family);
31
+ font-size: var(--bs-body-font-size);
32
+ box-shadow: none !important;
33
+ background: transparent !important;
34
+ }
35
+ </style>
36
+ <script>
37
+ import {Vue, Component, Model, Watch, Prop} from 'vue-property-decorator';
38
+ import itfTextField from '../../text-field/TextField.vue';
39
+ import itfSelect from '../../select/Select.vue';
40
+
41
+ export default @Component({
42
+ name: 'itfCustomizeInlineMultiselect',
43
+ components: {
44
+ itfTextField,
45
+ itfSelect
46
+ }
47
+ })
48
+
49
+ class itfCustomizeInlineMultiselect extends Vue {
50
+ @Model('input') value;
51
+ @Prop(Boolean) focused;
52
+ @Prop(Boolean) editable;
53
+ @Prop() field;
54
+ }
55
+ </script>
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="itf-inline-edit">
3
+ <div
4
+ :role="editable ? 'button' : 'textbox'"
5
+ tabindex="1"
6
+ ref="input"
7
+ @focus="onFocus"
8
+ @blur="onBlur"
9
+ @keydown.enter="onEnter"
10
+ @keydown.esc="onCancel"
11
+ class="notranslate"
12
+ spellcheck="true"
13
+ placeholder=" "
14
+ :contenteditable="editable"
15
+ v-html="value">
16
+ </div>
17
+ </div>
18
+ </template>
19
+ <style lang="scss" scoped>
20
+ .itf-inline-edit {
21
+ margin: -5px;
22
+ padding: 5px;
23
+ }
24
+ .notranslate {
25
+ border: 0 none;
26
+ margin: 0;
27
+ outline: 0;
28
+ resize: none;
29
+ background: transparent;
30
+ max-width: 100%;
31
+ width: 100%;
32
+ white-space: pre-wrap;
33
+ word-break: break-word;
34
+ caret-color: var(--bs-body-color);
35
+ }
36
+ </style>
37
+ <script>
38
+ import { Vue, Component, Model, Prop } from 'vue-property-decorator';
39
+ import itfTextField from '../../text-field/TextField.vue';
40
+
41
+ export default @Component({
42
+ name: 'itfCustomizeInlineText',
43
+ components: {
44
+ itfTextField
45
+ }
46
+ })
47
+
48
+ class itfCustomizeInlineText extends Vue {
49
+ @Model('input') value;
50
+ @Prop(Boolean) editable;
51
+
52
+ mounted() {
53
+ this.$refs.input.addEventListener('paste', function(e) {
54
+ // cancel paste
55
+ e.preventDefault();
56
+
57
+ // get text representation of clipboard
58
+ var text = (e.originalEvent || e).clipboardData.getData('text/plain');
59
+
60
+ // insert text manually
61
+ document.execCommand("insertHTML", false, text);
62
+ });
63
+ }
64
+
65
+ onEnter(event) {
66
+ if (event.shiftKey) {
67
+ return;
68
+ }
69
+ event.preventDefault();
70
+ event.stopPropagation();
71
+
72
+ this.$refs.input.blur();
73
+ }
74
+
75
+ onBlur() {
76
+ this.$emit('input', this.$refs.input.innerHTML);
77
+ this.$emit('blur');
78
+ }
79
+
80
+ onCancel(event) {
81
+ event.target.value = this.value;
82
+ this.$refs.input.blur();
83
+ }
84
+
85
+ onFocus() {
86
+ this.$emit('focus');
87
+ }
88
+ }
89
+ </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="itf-append-context">
2
+ <div class="itf-append-context" style="min-width: 300px">
3
3
  <div class="d-flex align-items-center px-3 py-2">
4
4
  <div class="me-1">
5
5
  <itf-icon-popover title="Icon" removable :value="value.Icon" @input="setParam({ Icon: $event })">
@@ -10,20 +10,77 @@
10
10
  </div>
11
11
 
12
12
  <div class="flex-grow-1">
13
- <itf-text-field :value="value.Name" @input="setParam({ Name: $event })" />
13
+ <itf-text-field ref="input" :value="value.Name" @input="setParam({ Name: $event })" />
14
14
  </div>
15
15
  </div>
16
16
  <div>
17
17
  <a class="dropdown-item d-flex justify-content-between" @click.prevent="$emit('edit')">
18
18
  <span>{{ $t('components.customize.type') }}</span>
19
- <span class="text-muted">{{ $t('components.customize.select') }} ></span>
19
+ <span class="text-muted">{{ itemType }} ></span>
20
20
  </a>
21
+ <ul class="itf-dropdown__menu shadow dropdown-menu dropdown-submenu">
22
+ <li><h6 class="dropdown-header">{{ $t('components.customize.type') }}</h6></li>
23
+ <li v-for="(type) of types">
24
+ <a class="dropdown-item d-flex align-items-center" href="" @click.prevent="setParam({ Type: type.Type })">
25
+ <itf-icon :name="type.Icon" :size="16" class="me-1" />
26
+ <span v-text="type.Name"></span>
27
+ <itf-icon v-if="value.Type === type.Type" name="check" />
28
+ </a>
29
+ </li>
30
+ </ul>
21
31
  </div>
32
+ <div v-if="value.Type === 'multiselect'" sortable-skip>
33
+ <div class="d-flex justify-content-between pe-2">
34
+ <h6 class="dropdown-header">{{ $t('components.customize.options') }}</h6>
35
+ <div v-if="value.Options && value.Options.length">
36
+ <itf-button icon small @click.prevent="addNewOption">
37
+ <itf-icon name="plus" />
38
+ </itf-button>
39
+ </div>
40
+ </div>
41
+
42
+ <div v-if="isNewOptionVisible" class="px-3">
43
+ <itf-text-field
44
+ ref="newOptionInput"
45
+ v-model="newOption"
46
+ placeholder="Type a new option..."
47
+ @blur="onBlurOption"
48
+ @keydown.enter="submitNewOption"
49
+ />
50
+ </div>
51
+ <div v-else-if="!value.Options || !value.Options.length" class="px-3">
52
+ <itf-button small block class="text-start" @click="addNewOption">
53
+ <itf-icon name="plus" />
54
+ {{ $t('components.customize.addOption') }}
55
+ </itf-button>
56
+ </div>
57
+
58
+ <sortable
59
+ :value="value.Options"
60
+ @input="setParam({ Options: $event })"
61
+ axis="y"
62
+ require-sortable-attribute
63
+ auto-scroll="window"
64
+ >
65
+ <div v-for="option of value.Options" class="dropdown-item d-flex justify-content-between" sortable>
66
+ <div>
67
+ <itf-icon class="dragHandle" name="drag_vertical" />
68
+ <span>{{ option.Name }}</span>
69
+ </div>
70
+ <span class="text-muted">></span>
71
+ </div>
72
+ </sortable>
73
+ </div>
74
+ <div><hr class="dropdown-divider"></div>
75
+ <div><a class="dropdown-item" @click.prevent="$emit('duplicate')"><itf-icon name="duplicate" /> {{ $t('components.customize.duplicateProperty') }}</a></div>
22
76
  <div>
23
- <a class="dropdown-item d-flex justify-content-between" @click.prevent="$emit('edit')">
24
- <span>{{ $t('components.customize.numberFormat') }}</span>
25
- <span class="text-muted">{{ $t('components.customize.money') }} ></span>
26
- </a>
77
+ <itf-delete-confirm-modal class="me-1" @delete="$emit('delete')">
78
+ <template #activator="{ on }">
79
+ <a class="dropdown-item text-danger" @click.prevent="on.click"><itf-icon name="trash" /> {{ $t('components.customize.deleteProperty') }}</a>
80
+ </template>
81
+ <h5 class="mb-0">{{$t('components.popover.confirmDelete')}}</h5>
82
+ <p class="mb-0">{{ $t('components.customize.areYouSureYouWantToDeleteThisField') }}</p>
83
+ </itf-delete-confirm-modal>
27
84
  </div>
28
85
  </div>
29
86
  </template>
@@ -32,22 +89,67 @@ import { Vue, Component, Model } from 'vue-property-decorator';
32
89
  import itfTextField from '../text-field/TextField.vue';
33
90
  import itfIcon from '../icon/Icon';
34
91
  import itfButton from '../button/Button';
92
+ import Sortable from '../sortable/Sortable';
35
93
  import itfIconPopover from '../popover/IconPopover.vue';
94
+ import {INLINE_TYPES} from './constants';
95
+ import itfDeleteConfirmModal from "../modal/DeleteConfirmModal.vue";
96
+ import PropertyItem from "./PropertyItem.vue";
36
97
 
37
98
  export default @Component({
38
99
  components: {
100
+ PropertyItem,
101
+ itfDeleteConfirmModal,
39
102
  itfButton,
40
103
  itfIcon,
41
104
  itfTextField,
42
- itfIconPopover
105
+ itfIconPopover,
106
+ Sortable
43
107
  }
44
108
  })
45
109
 
46
110
  class PropertiesEditMenu extends Vue {
47
111
  @Model('input') value;
48
112
 
113
+ isNewOptionVisible = false;
114
+ newOption = '';
115
+
116
+ get types() {
117
+ return INLINE_TYPES;
118
+ }
119
+
120
+ get itemType() {
121
+ const type = INLINE_TYPES.find((type) => type.Type === this.value.Type);
122
+ return type ? type.Name : '';
123
+ }
124
+
125
+ mounted() {
126
+ this.$nextTick(() => {
127
+ this.$refs.input.focus();
128
+ });
129
+ }
130
+
49
131
  setParam(obj) {
50
132
  this.$emit('input', { ...this.value, ...obj });
51
133
  }
134
+
135
+ onBlurOption() {
136
+ this.isNewOptionVisible = false;
137
+ }
138
+
139
+ addNewOption() {
140
+ this.isNewOptionVisible = true;
141
+ this.$nextTick(() => {
142
+ this.$refs.newOptionInput.focus();
143
+ });
144
+ }
145
+
146
+ submitNewOption() {
147
+ const newOption = this.newOption;
148
+ if (newOption) {
149
+ this.setParam({ Options: [{ Name: newOption }, ...(this.value.Options || [])] });
150
+ this.newOption = '';
151
+ this.isNewOptionVisible = false;
152
+ }
153
+ }
52
154
  }
53
155
  </script>