inviton-powerduck 0.0.129 → 0.0.132

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.
@@ -38,7 +38,10 @@ export default class LocalizedValueHelper {
38
38
  }
39
39
 
40
40
  for (const propKey in value) {
41
- return value[propKey];
41
+ const val = value[propKey];
42
+ if (val?.length > 0) {
43
+ return val;
44
+ }
42
45
  }
43
46
 
44
47
  return null as any;
@@ -891,7 +891,7 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
891
891
  }
892
892
  }
893
893
 
894
- markSelection(): void {
894
+ markSelection (): void {
895
895
  const mySelf = this;
896
896
  const escapeRegExp = function (string) {
897
897
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@@ -909,26 +909,55 @@ class DataTableComponent extends TsxComponent<DataTableArgs> implements DataTabl
909
909
  };
910
910
 
911
911
  let regexBuilder = '';
912
- const markArr: string[] = [];
912
+ const markArr = [];
913
913
  if (this.filterArr != null) {
914
914
  this.filterArr.forEach((filterItem) => {
915
915
  if (regexBuilder.length > 0) {
916
916
  regexBuilder += '|';
917
917
  }
918
918
 
919
+ const columnId = filterItem.PropertyName?.toLowerCase();
920
+ const tds = Array.from($(mySelf.$el).find(`td.dt-col-${columnId}`));
921
+ if (tds == null || tds.length == 0) {
922
+ return;
923
+ }
924
+
919
925
  (getValueArr(filterItem) || []).forEach((filterValue) => {
920
- markArr.push(escapeRegExp(filterValue));
926
+ markArr.push({
927
+ value: escapeRegExp(filterValue),
928
+ elements: tds,
929
+ });
921
930
  });
922
931
  });
923
932
  }
924
933
 
925
934
  if (!isNullOrEmpty(this.fullTextQuery)) {
926
- regexBuilder += escapeRegExp(this.fullTextQuery);
935
+ const value = escapeRegExp(this.fullTextQuery);
936
+ const tds = Array.from($(mySelf.$el).find(`td`));
937
+ regexBuilder += value;
938
+
939
+ markArr.push({
940
+ value,
941
+ elements: tds,
942
+ });
927
943
  }
928
944
 
929
945
  if (DataTableConfig.filterMarking) {
930
946
  this.unmarkSelection(() => {
931
- mySelf.markInstance.mark(markArr);
947
+ for (const item of markArr) {
948
+ this.markInstance.mark(item.value, {
949
+ filter: (node) => {
950
+ const parentTd = $(node).closest('td')[0];
951
+ if (!parentTd) {
952
+ return false;
953
+ }
954
+
955
+ const matchesColumn = item.elements?.some(el => parentTd?.isEqualNode(el));
956
+ const matchesText = parentTd?.textContent?.toLowerCase().includes(item.value.toLowerCase());
957
+ return matchesColumn && matchesText;
958
+ }
959
+ });
960
+ }
932
961
  });
933
962
  }
934
963
  }
@@ -40,6 +40,7 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
40
40
  @Prop() appendClicked: () => void;
41
41
  @Prop() prependClicked: () => void;
42
42
  currentValue: ILocalizedText = null;
43
+ editorHeight: number = null;
43
44
 
44
45
  raiseChangeEvent(newValue: ILocalizedText): void {
45
46
  this.populateValidationDeclaration();
@@ -59,6 +60,10 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
59
60
  this.raiseChangeEvent(value);
60
61
  }
61
62
 
63
+ onEditorResized (height: number): void {
64
+ this.editorHeight = height;
65
+ }
66
+
62
67
  getActualValue(lang: Language, value: ILocalizedText): string {
63
68
  if (value == null) {
64
69
  return null;
@@ -104,6 +109,8 @@ class LocalizedStringWysiwigComponent extends TsxComponent<LocalizedStringInputA
104
109
  key={lang.id}
105
110
  wrap={false}
106
111
  resizable={this.resizable}
112
+ initialHeight={this.editorHeight}
113
+ onResized={this.onEditorResized}
107
114
  label={null}
108
115
  value={this.getActualValue(lang.id, this.value)}
109
116
  changed={(e) => {
@@ -32,6 +32,8 @@ interface WysiwigEditorArgs extends FormItemWrapperArgs {
32
32
  useCommonUpload?: boolean;
33
33
  uploadPluginArgs?: () => any | any;
34
34
  resizable?: boolean;
35
+ initialHeight?: number;
36
+ onResized?: (height: number) => void;
35
37
  changed: (newValue: string) => void;
36
38
  }
37
39
 
@@ -56,8 +58,11 @@ class WysiwigEditorComponent extends TsxComponent<WysiwigEditorArgs> implements
56
58
  @Prop() appendClicked: () => void;
57
59
  @Prop() prependClicked: () => void;
58
60
  @Prop() changed: (newValue: string) => void;
61
+ @Prop() initialHeight: number = null;
62
+ @Prop() onResized?: (height: number) => void;
59
63
  @Prop({ default: true }) resizable: boolean;
60
64
  skipTrigger: boolean = false;
65
+ resizeObserver: ResizeObserver = null;
61
66
 
62
67
  raiseChangeEvent(value: string) {
63
68
  this.populateValidationDeclaration();
@@ -160,6 +165,25 @@ class WysiwigEditorComponent extends TsxComponent<WysiwigEditorArgs> implements
160
165
  innerEditor.addClass('vertical-resize');
161
166
  }
162
167
 
168
+ if (this.initialHeight) {
169
+ innerEditor.css('height', `${this.initialHeight}px`);
170
+ }
171
+
172
+ let lastNotifiedHeight = this.initialHeight || null;
173
+ if (this.onResized != null && this.resizable) {
174
+ this.resizeObserver = new ResizeObserver((entries) => {
175
+ for (const entry of entries) {
176
+ const height = entry?.contentRect?.height;
177
+ if (height > 0 && (lastNotifiedHeight == null || Math.abs(height - lastNotifiedHeight) > 2)) { // To keep height stable
178
+ lastNotifiedHeight = height;
179
+ this.onResized(height);
180
+ }
181
+ }
182
+ });
183
+
184
+ this.resizeObserver.observe(innerEditor?.[0]);
185
+ }
186
+
163
187
  innerEditor.on('click', (e) => {
164
188
  const clickTarget = e.target;
165
189
  if (clickTarget != null && clickTarget.nodeName == 'IMG') {
@@ -201,6 +225,13 @@ class WysiwigEditorComponent extends TsxComponent<WysiwigEditorArgs> implements
201
225
 
202
226
  beforeUnmount() {
203
227
  ($(this.$el) as any).trumbowyg('destroy');
228
+
229
+ if (this.resizeObserver != null) {
230
+ const innerEditor = $(this.$el).find('.trumbowyg-box');
231
+ if (innerEditor?.length > 0) {
232
+ this.resizeObserver.unobserve(innerEditor[0]);
233
+ }
234
+ }
204
235
  }
205
236
 
206
237
  buildImageDropdownArr(): string[] {
@@ -9,34 +9,34 @@
9
9
  padding-bottom: 20px;
10
10
  }
11
11
 
12
- .modal-header.modal-has-headericon > h5 {
12
+ .modal-header.modal-has-headericon>h5 {
13
13
  display: flex;
14
14
  width: 100%;
15
15
  min-height: 60px;
16
16
  align-items: center;
17
17
  }
18
18
 
19
- .modal-header.modal-has-headericon > .modal-header-icon {
19
+ .modal-header.modal-has-headericon>.modal-header-icon {
20
20
  position: absolute;
21
21
  top: 0;
22
22
  left: 22px;
23
23
  }
24
24
 
25
- .modal-header.modal-has-headericon > .modal-header-icon .swal2-transform-resize {
25
+ .modal-header.modal-has-headericon>.modal-header-icon .swal2-transform-resize {
26
26
  transform: scale(0.75);
27
27
  }
28
28
 
29
- .modal-header.modal-has-headericon > .modal-header-icon .swal2-transform-resize.swal2-icon,
30
- .modal-header.modal-has-headericon > .modal-header-icon .swal2-transform-resize .swal2-icon {
29
+ .modal-header.modal-has-headericon>.modal-header-icon .swal2-transform-resize.swal2-icon,
30
+ .modal-header.modal-has-headericon>.modal-header-icon .swal2-transform-resize .swal2-icon {
31
31
  margin-top: 10px;
32
32
  margin-left: -10px;
33
33
  }
34
34
 
35
- .modal-header.modal-has-headericon > .modal-header-icon .swal2-transform-resize .success-icon-stub {
35
+ .modal-header.modal-has-headericon>.modal-header-icon .swal2-transform-resize .success-icon-stub {
36
36
  border-color: #b9f0b7;
37
37
  }
38
38
 
39
- .modal-header.modal-has-headericon > .modal-header-icon .swal2-standard-resize {
39
+ .modal-header.modal-has-headericon>.modal-header-icon .swal2-standard-resize {
40
40
  font-size: 40px;
41
41
  line-height: 60px;
42
42
  text-align: center;
@@ -44,7 +44,7 @@
44
44
  height: 60px;
45
45
  }
46
46
 
47
- .modal-header.modal-has-headericon > .modal-header-icon > .swal2-icon {
47
+ .modal-header.modal-has-headericon>.modal-header-icon>.swal2-icon {
48
48
  overflow: hidden;
49
49
  }
50
50
 
@@ -59,3 +59,84 @@
59
59
  .modal-dialog.modal-ntl {
60
60
  max-width: 650px;
61
61
  }
62
+
63
+ @media(max-width:767.99px) {
64
+ .modal-bottom-sheet .modal-dialog {
65
+ position: fixed !important;
66
+ bottom: 0 !important;
67
+ margin: 0 !important;
68
+ width: 100% !important;
69
+ max-width: 100%;
70
+ transition: transform 0.3s ease-out !important;
71
+ }
72
+
73
+ .modal-bottom-sheet .modal-dialog .modal-content {
74
+ border-top-left-radius: 16px;
75
+ border-top-right-radius: 16px;
76
+ padding-top: 0;
77
+ }
78
+
79
+ /* Drag handle */
80
+ .modal-bottom-sheet .drag-handle {
81
+ width: 100%;
82
+ padding-top: 10px;
83
+ padding-bottom: 5px;
84
+ cursor: grab;
85
+ }
86
+
87
+ .modal-bottom-sheet .handle-bar {
88
+ width: 40px;
89
+ height: 4px;
90
+ background-color: #ccc;
91
+ border-radius: 2px;
92
+ margin: 0 auto;
93
+ }
94
+
95
+ .modal.modal-bottom-sheet.fade .modal-dialog {
96
+ transition: transform 0.3s ease-out, opacity 0.3s ease-out;
97
+ transform: translateY(100%);
98
+ opacity: 0;
99
+ }
100
+
101
+ .modal.modal-bottom-sheet.fade.show .modal-dialog {
102
+ transform: translateY(0);
103
+ opacity: 1;
104
+ }
105
+
106
+ .modal-is-dragging,
107
+ .modal-is-dragging * {
108
+ user-select: none;
109
+ }
110
+
111
+ .modal.modal-bottom-sheet.fade.modal-is-dragging .modal-dialog {
112
+ transition: none !important;
113
+ }
114
+
115
+ .modal.modal-bottom-sheet .modal-body {
116
+ max-height: 80vh;
117
+ overflow: auto;
118
+ }
119
+
120
+
121
+ .modal.fade.modal-mobile-fullscreen .modal-dialog {
122
+ margin: 0;
123
+ max-width: 100%;
124
+ border-radius: 0;
125
+ }
126
+
127
+ .modal.fade.modal-mobile-fullscreen .modal-dialog .modal-content,
128
+ .modal.fade.modal-mobile-fullscreen .modal-dialog .modal-header {
129
+ border-radius: 0;
130
+ border-top: 0;
131
+ border-left: 0;
132
+ border-right: 0;
133
+ }
134
+
135
+ .modal.fade.modal-mobile-fullscreen .modal-dialog .modal-footer {
136
+ position: sticky;
137
+ bottom: 0;
138
+ z-index: 1;
139
+ background: inherit;
140
+ }
141
+
142
+ }
@@ -18,14 +18,20 @@ interface ModalArgs {
18
18
  icon?: ModalHeaderIcon;
19
19
  blocked?: boolean;
20
20
  cssClass?: string;
21
+ mobileMode?: ModalMobileMode;
21
22
  dismissable?: boolean;
22
23
  backdropStatic?: boolean;
23
- mobileFullscreen?: boolean;
24
24
  modalLazyMode?: boolean;
25
25
  preventHistoryEntry?: boolean;
26
26
  portalTarget?: string;
27
27
  }
28
28
 
29
+ export enum ModalMobileMode {
30
+ Default = 0,
31
+ BottomSheetModal = 1,
32
+ FullScreen = 2,
33
+ }
34
+
29
35
  export enum ModalSize {
30
36
  Normal = 0,
31
37
  Small = 1,
@@ -52,6 +58,7 @@ declare global {
52
58
 
53
59
  export class ModalConfig {
54
60
  static defaultPortalTarget = 'body';
61
+ static defaultMobileMode = ModalMobileMode.BottomSheetModal;
55
62
  }
56
63
 
57
64
  @Component
@@ -63,10 +70,9 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
63
70
  @Prop() cssClass!: string;
64
71
  @Prop() dismissable: boolean;
65
72
  @Prop() backdropStatic!: boolean;
66
- @Prop() mobileFullscreen: boolean;
73
+ @Prop() mobileMode?: ModalMobileMode;
67
74
  @Prop() modalLazyMode: boolean;
68
75
  @Prop() preventHistoryEntry: boolean;
69
- @Prop() alternativePortal: boolean;
70
76
  uuid: string = null;
71
77
  modalShown: boolean = false;
72
78
 
@@ -98,6 +104,129 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
98
104
  }
99
105
  }
100
106
 
107
+ private getModalMobileModeCss(): string {
108
+ if (this.treatMobileAsBottomSheet()) {
109
+ return ' modal-bottom-sheet';
110
+ } else if (this.treatMobileAsFullScreen()) {
111
+ return ' modal-mobile-fullscreen';
112
+ }
113
+
114
+ return '';
115
+ }
116
+
117
+ private handleMobileModeDragBinding() {
118
+ if (!this.treatMobileAsBottomSheet()) {
119
+ return;
120
+ }
121
+
122
+ const modal = this.$refs.modalRoot as HTMLElement;
123
+ if ((modal as any)?._dragBound) {
124
+ return;
125
+ }
126
+
127
+ setTimeout(() => {
128
+ const modal = this.$refs.modalRoot as HTMLElement;
129
+ const modalDialog = modal.querySelector('.modal-dialog') as HTMLElement;
130
+ const dragHandle = modal.querySelector('.drag-handle');
131
+
132
+ let startY = 0;
133
+ let currentY = 0;
134
+ let isDragging = false;
135
+ const threshold = 100;
136
+ const cancelDragging = () => {
137
+ isDragging = false;
138
+ modal.classList.remove('modal-is-dragging');
139
+ };
140
+
141
+ const startDragging = () => {
142
+ isDragging = true;
143
+ modal.classList.add('modal-is-dragging');
144
+ };
145
+
146
+ const onMove = (y) => {
147
+ const translateY = Math.max(y - startY, 0);
148
+ modalDialog.style.transform = `translateY(${translateY}px)`;
149
+ };
150
+
151
+ const endDrag = () => {
152
+ cancelDragging();
153
+ const dragDistance = currentY - startY;
154
+
155
+ if (dragDistance > threshold) {
156
+ this.hide();
157
+ } else {
158
+ modalDialog.style.transform = 'translateY(0)';
159
+ }
160
+ };
161
+
162
+ // Touch events
163
+ dragHandle.addEventListener('touchstart', (e) => {
164
+ startY = (e as TouchEvent).touches[0].clientY;
165
+ startDragging();
166
+ });
167
+
168
+ dragHandle.addEventListener('touchmove', (e) => {
169
+ if (!isDragging) {
170
+ return;
171
+ }
172
+
173
+ currentY = (e as TouchEvent).touches[0].clientY;
174
+ onMove(currentY);
175
+ });
176
+
177
+ dragHandle.addEventListener('touchend', endDrag);
178
+
179
+ // Mouse events
180
+ dragHandle.addEventListener('mousedown', (e) => {
181
+ startY = (e as MouseEvent).clientY;
182
+ startDragging();
183
+
184
+ const onMouseMove = (e) => {
185
+ if (!isDragging) {
186
+ return;
187
+ }
188
+
189
+ currentY = e.clientY;
190
+ onMove(currentY);
191
+ e.preventDefault();
192
+ e.stopPropagation();
193
+ e.stopImmediatePropagation();
194
+ };
195
+
196
+ const onMouseUp = () => {
197
+ endDrag();
198
+ window.removeEventListener('mousemove', onMouseMove);
199
+ window.removeEventListener('mouseup', onMouseUp);
200
+ };
201
+
202
+ window.addEventListener('mousemove', onMouseMove);
203
+ window.addEventListener('mouseup', onMouseUp);
204
+ });
205
+
206
+ // Reset on modal hide
207
+ modal.addEventListener('hidden.bs.modal', () => {
208
+ cancelDragging();
209
+ modalDialog.style.transform = '';
210
+ });
211
+
212
+ (modal as any)._dragBound = true;
213
+ }, 100);
214
+ }
215
+
216
+ treatMobileAsBottomSheet(): boolean {
217
+ if (this.mobileMode == ModalMobileMode.BottomSheetModal) {
218
+ return true;
219
+ } else if (this.mobileMode == null && ModalConfig.defaultMobileMode == ModalMobileMode.BottomSheetModal) {
220
+ return true;
221
+ }
222
+
223
+ return false;
224
+ }
225
+
226
+ treatMobileAsFullScreen(): boolean {
227
+ return (this.mobileMode == ModalMobileMode.FullScreen || (this.mobileMode == null && ModalConfig.defaultMobileMode == ModalMobileMode.FullScreen));
228
+ }
229
+
101
230
  getModalInstance(): BootstrapModal {
102
231
  return BootstrapModal.getOrCreateInstance(this.$refs.modalRoot as Element);
103
232
  }
@@ -112,6 +241,8 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
112
241
  onHidden: args.onHidden,
113
242
  onBeforeShown: args.onBeforeShown,
114
243
  });
244
+
245
+ this.handleMobileModeDragBinding();
115
246
  });
116
247
  }
117
248
 
@@ -133,7 +264,7 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
133
264
  id={this.getFullId()}
134
265
  key={this.getFullId()}
135
266
  ref="modalRoot"
136
- class={`modal fade ${this.mobileFullscreen ? 'mobile-fullscreen ' : ''}${this.cssClass || ''}`}
267
+ class={`modal fade${this.getModalMobileModeCss()} ${this.cssClass || ''}`}
137
268
  role="dialog"
138
269
  aria-labelledby={this.getLabelId()}
139
270
  aria-hidden="true"
@@ -144,6 +275,10 @@ class ModalComponent extends TsxComponent<ModalArgs> implements ModalArgs {
144
275
  >
145
276
  <div class={`modal-dialog${this.getModalSizeCss()}`} role="document">
146
277
  <div class="modal-content">
278
+ <div class="drag-handle text-center">
279
+ <div class="handle-bar"></div>
280
+ </div>
281
+
147
282
  <LoadingIndicator visible={this.blocked} />
148
283
  <div class={`modal-header${this.icon == null ? '' : ' modal-has-headericon'}`}>
149
284
  {this.icon != null && <div class="modal-header-icon">{this.renderModalHeaderIcon()}</div>}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
3
  "type": "module",
4
- "version": "0.0.129",
4
+ "version": "0.0.132",
5
5
  "files": [
6
6
  "app/",
7
7
  "common/",