@5minds/node-red-dashboard-2-processcube-dynamic-table 2.0.1-feature-a2a44b-mbtbbouc → 2.0.1-feature-d958b6-mcew1who

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.
@@ -1,40 +1,54 @@
1
1
  <template>
2
- <v-data-table :headers="headers" :items="tasks" :search="search" :sort-by="sortBy" :items-per-page="itemsPerPage"
3
- :items-per-page-options="itemsPerPageOptions" @update:sort-by="updateSortBy"
4
- @update:items-per-page="updateItemsPerPage" class="full-width-table">
5
- <template v-slot:top>
6
- <v-toolbar flat class="py-2">
7
- <v-text-field class="mx-3 search-input" v-model="search" label="Search" prepend-inner-icon="mdi-magnify"
8
- variant="outlined" hide-details single-line></v-text-field>
9
- </v-toolbar>
10
- </template>
11
- <template v-slot:item.actions="{ item }">
12
- <v-container class="action-button-container">
13
- <v-container v-for="(action, index) in actions"
14
- style="padding: 0px; display: inline; width: fit-content; margin: 0px">
15
- <v-btn style="margin: 0px 2px" class="action-button" v-if="conditionCheck(action.condition, item)"
16
- :key="index" size="small" @click="actionFn(action, item)">
17
- {{ action.label }}
2
+ <div class="ui-dynamic-table-container">
3
+ <UIDynamicTableTitleText v-if="props.title_text && props.title_text.length > 0" :title="props.title_text"
4
+ :style="props.title_style || 'default'" :customStyles="props.title_custom_text_styling || ''"
5
+ :titleIcon="props.title_icon || ''" />
6
+ <v-data-table :headers="headers" :items="tasks" :search="search" class="full-width-table">
7
+ <template v-slot:top>
8
+ <v-toolbar flat class="pb-2 pt-8">
9
+ <v-text-field class="mx-3 search-input" v-model="search" label="Suchen"
10
+ prepend-inner-icon="mdi-magnify" variant="outlined" hide-details single-line></v-text-field>
11
+ <v-btn class="reload-button ml-2 mr-8" @click="reloadData" :disabled="isReloading" size="small"
12
+ variant="outlined">
13
+ <v-icon left :class="{ 'reload-spin': isReloading }">mdi-refresh</v-icon>
14
+ Neu Laden
18
15
  </v-btn>
16
+ </v-toolbar>
17
+ </template>
18
+ <template v-slot:item.actions="{ item }">
19
+ <v-container class="action-button-container">
20
+ <v-container v-for="(action, index) in actions"
21
+ style="padding: 0px; display: inline; width: fit-content; margin: 0px">
22
+ <v-btn style="margin: 0px 2px" class="action-button"
23
+ v-if="conditionCheck(action.condition, item)" :key="index" size="small"
24
+ @click="actionFn(action, item)">
25
+ {{ action.label }}
26
+ </v-btn>
27
+ </v-container>
19
28
  </v-container>
20
- </v-container>
21
- </template>
22
- <template v-slot:no-data>
23
- <v-alert text="No Data" style="margin: 12px"></v-alert>
24
- </template>
25
- </v-data-table>
29
+ </template>
30
+ <template v-slot:no-data>
31
+ <v-alert text="No Data" style="margin: 12px"></v-alert>
32
+ </template>
33
+ </v-data-table>
34
+ </div>
26
35
  </template>
27
36
 
28
37
  <script>
29
38
  import { mapState } from 'vuex';
30
- import { debounce } from 'lodash';
39
+ import UIDynamicTableTitleText from './TitleText.vue'
31
40
 
32
41
  export default {
33
42
  name: 'UIDynamicTable',
43
+ components: {
44
+ UIDynamicTableTitleText
45
+ },
34
46
  inject: ['$socket'],
35
47
  props: {
48
+ /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
36
49
  id: { type: String, required: true },
37
50
  props: { type: Object, default: () => ({}) },
51
+ /* state: { type: Object, default: () => ({ enabled: false, visible: false }) } // DEFAULT */
38
52
  state: {
39
53
  type: Object,
40
54
  default: () => ({ enabled: true, visible: true }),
@@ -42,15 +56,6 @@ export default {
42
56
  },
43
57
  computed: {
44
58
  ...mapState('data', ['messages']),
45
- searchParamKey() {
46
- return `search_${this.id}`;
47
- },
48
- sortByStorageKey() {
49
- return `table_sort_${this.id}`;
50
- },
51
- itemsPerPageStorageKey() {
52
- return `table_items_per_page_${this.id}`;
53
- },
54
59
  },
55
60
  data() {
56
61
  return {
@@ -58,24 +63,10 @@ export default {
58
63
  actions: [],
59
64
  tasks: [],
60
65
  headers: [],
61
- isInitialized: false,
62
- sortBy: [],
63
- itemsPerPage: 10,
64
- itemsPerPageOptions: [
65
- { value: 5, title: '5' },
66
- { value: 10, title: '10' },
67
- { value: 25, title: '25' },
68
- { value: 50, title: '50' },
69
- { value: 100, title: '100' },
70
- { value: -1, title: 'All' }
71
- ],
66
+ isReloading: false,
72
67
  };
73
68
  },
74
69
  mounted() {
75
- this.loadFromLocalStorage();
76
- this.loadSearchFromUrl();
77
- this.isInitialized = true;
78
-
79
70
  this.$socket.on('widget-load:' + this.id, (msg) => {
80
71
  this.initialize(msg);
81
72
  this.$store.commit('data/bind', {
@@ -89,7 +80,9 @@ export default {
89
80
  }
90
81
 
91
82
  this.initialize(msg);
83
+
92
84
  this.messages[this.id] = msg;
85
+
93
86
  this.$store.commit('data/bind', {
94
87
  widgetId: this.id,
95
88
  msg,
@@ -128,7 +121,6 @@ export default {
128
121
  title: 'Actions',
129
122
  align: 'center',
130
123
  key: 'actions',
131
- sortable: false
132
124
  });
133
125
  },
134
126
  conditionCheck(condition, row) {
@@ -141,82 +133,19 @@ export default {
141
133
  return false;
142
134
  }
143
135
  },
144
- updateSearchParam: debounce(function (searchValue) {
145
- const currentQuery = { ...this.$route.query };
136
+ reloadData() {
137
+ this.isReloading = true;
138
+ this.$socket.emit('widget-load', this.id);
146
139
 
147
- if (searchValue && searchValue.trim() !== '') {
148
- currentQuery[this.searchParamKey] = searchValue;
149
- } else {
150
- delete currentQuery[this.searchParamKey];
151
- }
152
-
153
- this.$router.replace({
154
- path: this.$route.path,
155
- query: currentQuery
156
- }).catch(err => {
157
- if (err.name !== 'NavigationDuplicated') {
158
- console.error('Router navigation error:', err);
159
- }
160
- });
161
- }, 500),
162
- loadSearchFromUrl() {
163
- const searchValue = this.$route.query[this.searchParamKey];
164
- if (searchValue && typeof searchValue === 'string') {
165
- this.search = searchValue;
166
- } else {
167
- this.search = '';
168
- }
169
- },
170
- loadFromLocalStorage() {
171
- try {
172
- const savedSortBy = localStorage.getItem(this.sortByStorageKey);
173
- if (savedSortBy) {
174
- this.sortBy = JSON.parse(savedSortBy);
175
- }
176
-
177
- const savedItemsPerPage = localStorage.getItem(this.itemsPerPageStorageKey);
178
- if (savedItemsPerPage) {
179
- this.itemsPerPage = parseInt(savedItemsPerPage, 10);
180
- }
181
- } catch (error) {
182
- console.error('Error loading table settings from localStorage:', error);
183
- }
184
- },
185
- saveToLocalStorage() {
186
- try {
187
- localStorage.setItem(this.sortByStorageKey, JSON.stringify(this.sortBy));
188
- localStorage.setItem(this.itemsPerPageStorageKey, this.itemsPerPage.toString());
189
- } catch (error) {
190
- console.error('Error saving table settings to localStorage:', error);
191
- }
192
- },
193
- updateSortBy(newSortBy) {
194
- this.sortBy = newSortBy;
195
- this.saveToLocalStorage();
196
- },
197
- updateItemsPerPage(newItemsPerPage) {
198
- this.itemsPerPage = newItemsPerPage;
199
- this.saveToLocalStorage();
200
- },
201
- },
202
- watch: {
203
- search(newValue, oldValue) {
204
- if (this.isInitialized) {
205
- this.updateSearchParam(newValue);
206
- }
140
+ setTimeout(() => {
141
+ this.isReloading = false;
142
+ }, 1000);
207
143
  },
208
- '$route.query': {
209
- handler(newQuery) {
210
- if (this.isInitialized) {
211
- this.loadSearchFromUrl();
212
- }
213
- },
214
- deep: true
215
- }
216
144
  },
217
145
  };
218
146
  </script>
219
147
 
220
148
  <style scoped>
149
+ /* CSS is auto scoped, but using named classes is still recommended */
221
150
  @import '../stylesheets/ui-dynamic-table.css';
222
151
  </style>
@@ -1,96 +1,126 @@
1
1
  h1 {
2
- margin-bottom: 10px
2
+ margin-bottom: 10px;
3
3
  }
4
4
 
5
5
  h2 {
6
- margin-top: 1.5rem;
7
- margin-bottom: 0.75rem;
6
+ margin-top: 1.5rem;
7
+ margin-bottom: 0.75rem;
8
8
  }
9
9
 
10
10
  h3 {
11
- margin-top: 1rem;
11
+ margin-top: 1rem;
12
12
  }
13
13
 
14
14
  p {
15
- margin-bottom: 5px;
15
+ margin-bottom: 5px;
16
16
  }
17
17
 
18
18
  ul li {
19
- list-style-type: circle;
20
- list-style-position: inside;
21
- margin-left: 15px;
19
+ list-style-type: circle;
20
+ list-style-position: inside;
21
+ margin-left: 15px;
22
22
  }
23
23
 
24
24
  pre {
25
- padding: 12px;
26
- margin: 12px;
27
- background-color: #eee;
25
+ padding: 12px;
26
+ margin: 12px;
27
+ background-color: #eee;
28
28
  }
29
29
 
30
30
  code {
31
- font-size: 0.825rem;
32
- color: #ae0000;
31
+ font-size: 0.825rem;
32
+ color: #ae0000;
33
33
  }
34
34
 
35
35
  input {
36
- padding: 12px;
37
- margin: 12px;
38
- background-color: #eee;
36
+ padding: 12px;
37
+ margin: 12px;
38
+ background-color: #eee;
39
39
  }
40
40
 
41
41
  .task-div {
42
- padding: 8px;
43
- margin: 16px;
44
- border: solid;
45
- border-radius: 4px;
46
- border-color: #303030;
42
+ padding: 8px;
43
+ margin: 16px;
44
+ border: solid;
45
+ border-radius: 4px;
46
+ border-color: #303030;
47
47
  }
48
48
 
49
49
  .action-button-container {
50
- width: 100%;
51
- display: flex;
52
- justify-content: center;
50
+ width: 100%;
51
+ display: flex;
52
+ justify-content: center;
53
53
  }
54
54
 
55
55
  .full-width-table {
56
56
  --v-table-header-height: 40px !important;
57
+ background-color: #fff;
58
+ border-radius: 0px 0px 8px 8px;
57
59
  }
58
60
 
59
61
  .full-width-table .v-toolbar {
60
- background: unset !important;
61
- color: unset !important;
62
+ background: unset !important;
63
+ color: unset !important;
62
64
  }
63
65
 
64
66
  .full-width-table .v-table__wrapper .v-alert--variant-flat {
65
- background: unset !important;
66
- color: unset !important;
67
+ background: unset !important;
68
+ color: unset !important;
67
69
  }
68
70
 
69
- .full-width-table >>> thead th {
71
+ .full-width-table :deep(thead th) {
70
72
  border-bottom: 4px solid rgb(var(--v-theme-primary)) !important;
71
73
  font-weight: 700 !important;
72
74
  font-size: 16px !important;
73
75
  }
74
76
 
75
- .full-width-table >>> tbody {
76
- background-color: #F6F5FA !important;
77
+ .full-width-table :deep(tbody) {
78
+ background-color: unset !important;
77
79
  --widget-row-height: 40px !important;
78
80
  font-size: 16px;
79
81
  }
80
82
 
81
- .search-input >>> .v-field__outline__end {
82
- border-right: none;
83
- border-top: none;
84
- border-radius: 0px;
83
+ .search-input :deep(.v-field__outline__end) {
84
+ border: none !important;
85
+ }
86
+
87
+ .search-input :deep(.v-field__outline__start) {
88
+ border: none !important;
89
+ }
90
+
91
+ .search-input :deep(.v-field__outline) {
92
+ display: none !important;
85
93
  }
86
94
 
87
- .search-input >>> .v-label {
95
+ .search-input :deep(.v-label) {
88
96
  font-size: 14px !important;
89
97
  }
90
98
 
91
- .search-input >>> .v-field__outline__start {
99
+ .search-input :deep(.v-field) {
92
100
  border-radius: 0px 0px 0px 8px;
93
- border-top: none;
101
+ border-bottom: 2px solid rgb(var(--v-theme-primary)) !important;
102
+ border-left: 2px solid rgb(var(--v-theme-primary)) !important;
103
+ border-right: none !important;
104
+ border-top: none !important;
105
+ transition: border-width 0.1s ease-in-out;
106
+ margin-bottom: 8px;
107
+ }
108
+
109
+ .search-input :deep(.v-field__input) {
110
+ font-size: 18px;
111
+ font-weight: 400;
112
+ font-style: normal;
113
+ padding: 4px 8px;
114
+ }
115
+
116
+ .search-input:focus-within :deep(.v-field) {
117
+ border-bottom: 4px solid rgb(var(--v-theme-primary)) !important;
118
+ border-left: 4px solid rgb(var(--v-theme-primary)) !important;
119
+ }
120
+
121
+ .search-input:focus-within :deep(.v-field__input) {
122
+ background-color: #f6f5fa;
123
+ border-radius: 0 8px 0px 5px;
94
124
  }
95
125
 
96
126
  .action-button {
@@ -101,3 +131,69 @@ input {
101
131
  color: rgb(var(--v-theme-primary));
102
132
  padding: 8px;
103
133
  }
134
+
135
+ .ui-dynamic-table-container {
136
+ border-radius: 8px;
137
+ border-bottom: 4px solid rgb(var(--v-theme-primary));
138
+ box-shadow: 0 4px 16px -3px rgba(0, 0, 0, 0.5);
139
+ }
140
+
141
+ .ui-dynamic-table-title-default {
142
+ background: linear-gradient(131deg, #18181a 26.76%, #242326 100.16%);
143
+ padding: 32px;
144
+ color: #ffffff;
145
+ font-size: 36px;
146
+ font-weight: 700;
147
+ border-radius: 8px 8px 0px 0px;
148
+ margin-top: 0px;
149
+ border: 2px solid #f6f5fa;
150
+ border-bottom: none;
151
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
152
+ }
153
+
154
+ .ui-dynamic-table-title-default hr {
155
+ border: 4px solid rgb(var(--v-theme-primary));
156
+ border-radius: 2px;
157
+ }
158
+
159
+ .ui-dynamic-table-title-minimal {
160
+ padding: 16px;
161
+ margin-top: 0px;
162
+ border: 2px solid #f6f5fa;
163
+ border-bottom: none;
164
+ }
165
+
166
+ .ui-dynamic-table-title-outside {
167
+ padding-bottom: 24px;
168
+ padding-top: 24px;
169
+ font-size: 24px;
170
+ font-weight: 700;
171
+ }
172
+
173
+ .reload-button {
174
+ border-radius: 8px !important;
175
+ background-color: transparent !important;
176
+ border-color: rgb(var(--v-theme-primary)) !important;
177
+ border: 2px solid !important;
178
+ color: rgb(var(--v-theme-primary)) !important;
179
+ padding: 8px !important;
180
+ min-height: 36px !important;
181
+ font-weight: 500 !important;
182
+ }
183
+
184
+ .reload-button:hover {
185
+ background-color: rgba(var(--v-theme-primary), 0.08) !important;
186
+ }
187
+
188
+ .reload-button:disabled {
189
+ opacity: 0.6 !important;
190
+ }
191
+
192
+ .reload-spin {
193
+ animation: spin 1s linear infinite;
194
+ }
195
+
196
+ @keyframes spin {
197
+ from { transform: rotate(0deg); }
198
+ to { transform: rotate(360deg); }
199
+ }