glib-web 6.0.3 → 6.0.4
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/action.js +2 -0
- package/actions/lists/remove.js +23 -0
- package/app.scss +14 -1
- package/components/_internal_button.vue +2 -44
- package/components/_internal_icon.vue +5 -10
- package/components/composable/listAutoload.js +32 -0
- package/components/composable/tableAutoload.js +33 -0
- package/components/fab.vue +19 -9
- package/components/fields/_patternText.vue +100 -19
- package/components/fields/_select.vue +8 -1
- package/components/fields/text.vue +12 -1
- package/components/mixins/buttonProps.js +30 -0
- package/components/mixins/longClick.js +13 -7
- package/components/mixins/scrolling.js +2 -2
- package/components/mixins/styles.js +8 -0
- package/components/panels/custom.vue +10 -7
- package/components/panels/list.vue +42 -10
- package/components/panels/table.vue +1 -76
- package/components/validation.js +24 -19
- package/cypress/e2e/glib-web/fields.cy.js +29 -0
- package/cypress/e2e/glib-web/fieldsDateTime.cy.js +67 -0
- package/cypress/e2e/glib-web/fieldsSelect.cy.js +48 -0
- package/cypress/e2e/glib-web/list.cy.js +103 -10
- package/cypress/support/component.js +1 -1
- package/cypress/support/e2e.js +1 -1
- package/package.json +1 -1
- package/plugins/vuetify.js +2 -1
- package/templates/comment.vue +8 -0
- package/templates/editable.vue +6 -1
- package/templates/featured.vue +7 -0
- package/templates/thumbnail.vue +8 -2
- package/utils/constant.js +9 -1
- package/components/composable/tableExport.js +0 -84
- package/components/composable/tableImport.js +0 -163
- package/cypress/e2e/glib-web/listsAppend.cy.js +0 -27
package/templates/editable.vue
CHANGED
|
@@ -162,8 +162,13 @@ export default {
|
|
|
162
162
|
// },
|
|
163
163
|
},
|
|
164
164
|
methods: {
|
|
165
|
+
// Enable so the row's `id` registers in GLib.component, allowing
|
|
166
|
+
// `components/replace`, `logics/set` and `lists/remove` to target the row.
|
|
167
|
+
// When the row delegates its `id` to an inner check field (form/edit mode),
|
|
168
|
+
// don't also register the row under the same id — that duplicate would shadow
|
|
169
|
+
// the checkbox and break `logics/set targetId: <rowId>` aimed at it.
|
|
165
170
|
$registryEnabled() {
|
|
166
|
-
return
|
|
171
|
+
return !this.checkSpec;
|
|
167
172
|
},
|
|
168
173
|
buttonSpec(item) {
|
|
169
174
|
// Classes should be coming from the backend
|
package/templates/featured.vue
CHANGED
|
@@ -28,6 +28,13 @@ export default {
|
|
|
28
28
|
editButtons() {
|
|
29
29
|
return this.spec.editButtons || [];
|
|
30
30
|
}
|
|
31
|
+
},
|
|
32
|
+
methods: {
|
|
33
|
+
// Enable so the row's `id` registers in GLib.component, allowing
|
|
34
|
+
// `components/replace`, `logics/set` and `lists/remove` to target the row.
|
|
35
|
+
$registryEnabled() {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
};
|
|
33
40
|
</script>
|
package/templates/thumbnail.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<component :is="componentName" :href="$href()" class="thumbnail" :class="cssClasses" @[clickCondition]="$onClick()">
|
|
2
|
+
<component :is="componentName" v-ripple="!!clickCondition" :href="$href()" class="thumbnail" :class="cssClasses" @[clickCondition]="$onClick()">
|
|
3
3
|
<panels-responsive v-if="spec.header" :spec="spec.header" />
|
|
4
4
|
<div style="display:flex;">
|
|
5
5
|
<!-- <div v-if="spec.leftOuterButtons" style="display:flex; margin-top:10px;">
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
</div> -->
|
|
12
12
|
|
|
13
13
|
<panels-responsive v-if="spec.left" :spec="spec.left" />
|
|
14
|
-
<v-list-item v-longclick="$onLongPress" class="item-content" :style="columnStyles()">
|
|
14
|
+
<v-list-item v-longclick="$onLongPress" :ripple="false" class="item-content" :style="columnStyles()">
|
|
15
15
|
<!-- <v-icon v-if="spec.onReorder" class="handle">drag_indicator</v-icon> -->
|
|
16
16
|
|
|
17
17
|
<template #prepend>
|
|
@@ -199,6 +199,11 @@ export default {
|
|
|
199
199
|
// },
|
|
200
200
|
},
|
|
201
201
|
methods: {
|
|
202
|
+
// Enable so the row's `id` registers in GLib.component, allowing
|
|
203
|
+
// `components/replace`, `logics/set` and `lists/remove` to target the row.
|
|
204
|
+
$registryEnabled() {
|
|
205
|
+
return true;
|
|
206
|
+
},
|
|
202
207
|
buttonSpec(item) {
|
|
203
208
|
// Classes should be coming from the backend
|
|
204
209
|
// const styleClasses = ["text", "x-small"]
|
|
@@ -285,6 +290,7 @@ a.thumbnail {
|
|
|
285
290
|
color: inherit;
|
|
286
291
|
text-decoration: inherit;
|
|
287
292
|
display: block;
|
|
293
|
+
cursor: pointer;
|
|
288
294
|
}
|
|
289
295
|
|
|
290
296
|
.item-content {
|
package/utils/constant.js
CHANGED
|
@@ -46,5 +46,13 @@ function determineSize(styleClasses, defaultSize) {
|
|
|
46
46
|
return defaultSize;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
function resolveIconName(iconSpec) {
|
|
50
|
+
if (!iconSpec) return undefined;
|
|
51
|
+
if (typeof iconSpec === 'string') return iconSpec;
|
|
52
|
+
if (iconSpec.fa) return `fab fa-${iconSpec.fa.name}`;
|
|
53
|
+
if (iconSpec.material) return iconSpec.material.name;
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { variants, determineColor, determineVariant, determineDensity, determineSize, resolveIconName }
|
|
50
58
|
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { getCurrentInstance, ref } from "vue";
|
|
2
|
-
import { isString, isObject } from "../../utils/type";
|
|
3
|
-
|
|
4
|
-
const getProxy = (instance) => instance?.proxy || null;
|
|
5
|
-
|
|
6
|
-
function useTableExport() {
|
|
7
|
-
const instance = getCurrentInstance();
|
|
8
|
-
const exportable = ref(false);
|
|
9
|
-
const exportLabel = ref("CSV");
|
|
10
|
-
const exportFile = ref("output");
|
|
11
|
-
|
|
12
|
-
const initCsvExport = () => {
|
|
13
|
-
let exportName = "output";
|
|
14
|
-
const proxy = getProxy(instance);
|
|
15
|
-
if (!proxy) return;
|
|
16
|
-
|
|
17
|
-
const exportSpec = proxy.spec?.export;
|
|
18
|
-
if (isObject(exportSpec)) {
|
|
19
|
-
exportable.value = true;
|
|
20
|
-
|
|
21
|
-
if (isString(exportSpec.label)) {
|
|
22
|
-
exportLabel.value = exportSpec.label;
|
|
23
|
-
}
|
|
24
|
-
if (isString(exportSpec.fileName)) {
|
|
25
|
-
exportName = exportSpec.fileName;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exportFile.value = `${exportName}.csv`;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const exportCsv = (section) => {
|
|
33
|
-
return (
|
|
34
|
-
"data:text/csv;charset=" +
|
|
35
|
-
document.characterSet +
|
|
36
|
-
"," +
|
|
37
|
-
encodeURI(compileCsvRows(section))
|
|
38
|
-
);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const compileCsvRows = (section) => {
|
|
42
|
-
const lines = [];
|
|
43
|
-
lines.push(compileCsvRow(section.header));
|
|
44
|
-
for (const row of section.rows) {
|
|
45
|
-
lines.push(compileCsvRow(row));
|
|
46
|
-
}
|
|
47
|
-
return lines.join("\n");
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const compileCsvRow = (row) => {
|
|
51
|
-
return row.cellViews.map((view) => escapeForCsv(view.text)).join(",");
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const escapeForCsv = (text) => {
|
|
55
|
-
if (isString(text)) {
|
|
56
|
-
if (text === "") {
|
|
57
|
-
return '""';
|
|
58
|
-
}
|
|
59
|
-
if (
|
|
60
|
-
text.contains("\"") ||
|
|
61
|
-
text.contains("\n") ||
|
|
62
|
-
text.contains("\r") ||
|
|
63
|
-
text.contains(",")
|
|
64
|
-
) {
|
|
65
|
-
return '"' + text.replace(/"/g, '""') + '"';
|
|
66
|
-
}
|
|
67
|
-
return text;
|
|
68
|
-
}
|
|
69
|
-
return '""';
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
exportable,
|
|
74
|
-
exportLabel,
|
|
75
|
-
exportFile,
|
|
76
|
-
initCsvExport,
|
|
77
|
-
exportCsv,
|
|
78
|
-
_compileCsvRows: compileCsvRows,
|
|
79
|
-
_compileCsvRow: compileCsvRow,
|
|
80
|
-
_escapeForCsv: escapeForCsv
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export { useTableExport };
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { getCurrentInstance, ref } from "vue";
|
|
2
|
-
import { getAllFormData } from "./form";
|
|
3
|
-
import { isObject, isString } from "../../utils/type";
|
|
4
|
-
|
|
5
|
-
const getProxy = (instance) => instance?.proxy || null;
|
|
6
|
-
|
|
7
|
-
function useTableImport() {
|
|
8
|
-
const instance = getCurrentInstance();
|
|
9
|
-
const importable = ref(false);
|
|
10
|
-
const importSubmitUrl = ref(null);
|
|
11
|
-
const importParamName = ref("row");
|
|
12
|
-
|
|
13
|
-
const initCsvImport = () => {
|
|
14
|
-
const proxy = getProxy(instance);
|
|
15
|
-
if (!proxy) return;
|
|
16
|
-
|
|
17
|
-
const importSpec = proxy.spec?.import;
|
|
18
|
-
if (isObject(importSpec)) {
|
|
19
|
-
importable.value = true;
|
|
20
|
-
importSubmitUrl.value = importSpec.submitUrl;
|
|
21
|
-
importParamName.value = importSpec.paramName;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const rowSelected = (sectionIndex, rowIndex) => {
|
|
26
|
-
return getAllFormData()[rowCheckId(sectionIndex, rowIndex)];
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const rowCheckId = (sectionIndex, rowIndex) => {
|
|
30
|
-
return `row_${sectionIndex}_${rowIndex}`;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const selectedRowCount = (section) => {
|
|
34
|
-
const sectionIndex = section.index;
|
|
35
|
-
let count = 0;
|
|
36
|
-
section.dataRows.forEach((_row, rowIndex) => {
|
|
37
|
-
if (rowSelected(sectionIndex, rowIndex)) {
|
|
38
|
-
count++;
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
return count;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const submitRows = (_event, section) => {
|
|
45
|
-
const keys = section.header.dataCells;
|
|
46
|
-
const rows = [];
|
|
47
|
-
const sectionIndex = section.index;
|
|
48
|
-
let count = 0;
|
|
49
|
-
section.dataRows.forEach((row, rowIndex) => {
|
|
50
|
-
if (!rowSelected(sectionIndex, rowIndex)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
count++;
|
|
55
|
-
const cells = {};
|
|
56
|
-
row.forEach((cell, cellIndex) => {
|
|
57
|
-
const key = `${importParamName.value}[${keys[cellIndex]}]`;
|
|
58
|
-
cells[key] = cell;
|
|
59
|
-
});
|
|
60
|
-
cells["_index"] = rowIndex;
|
|
61
|
-
rows.push(cells);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (count > 0) {
|
|
65
|
-
submitEachRow(rows);
|
|
66
|
-
} else {
|
|
67
|
-
Utils.launch.dialog.alert("Please select at least one row.", getProxy(instance));
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const submitEachRow = (rows) => {
|
|
72
|
-
const url = Utils.type.string(importSubmitUrl.value);
|
|
73
|
-
if (!url) return;
|
|
74
|
-
|
|
75
|
-
const proxy = getProxy(instance);
|
|
76
|
-
const row = rows.shift();
|
|
77
|
-
if (row) {
|
|
78
|
-
const data = {
|
|
79
|
-
url: url,
|
|
80
|
-
formData: row
|
|
81
|
-
};
|
|
82
|
-
Utils.http.execute(data, "POST", proxy, (response) => {
|
|
83
|
-
GLib.action.handleResponse(response, proxy);
|
|
84
|
-
submitEachRow(rows);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const loadFile = (file, section) => {
|
|
90
|
-
const reader = new FileReader();
|
|
91
|
-
reader.readAsText(file);
|
|
92
|
-
reader.onload = (e) => {
|
|
93
|
-
const rows = parseCsv(e.target.result);
|
|
94
|
-
section.header.dataCells = rows.shift().map((cell) => {
|
|
95
|
-
return cell.trim();
|
|
96
|
-
});
|
|
97
|
-
section.dataRows = rows;
|
|
98
|
-
|
|
99
|
-
if (rows.length > 0) {
|
|
100
|
-
const proxy = getProxy(instance);
|
|
101
|
-
proxy.fileLoaded = true;
|
|
102
|
-
GLib.action.execute(proxy.spec.onLoadRows, proxy);
|
|
103
|
-
} else {
|
|
104
|
-
Utils.launch.dialog.alert("File doesn't contain valid data.", getProxy(instance));
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
event.target.value = null;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const parseCsv = (csvString) => {
|
|
112
|
-
let prevLetter = "",
|
|
113
|
-
row = [""],
|
|
114
|
-
result = [row],
|
|
115
|
-
columnIndex = 0,
|
|
116
|
-
rowIndex = 0,
|
|
117
|
-
canSplit = true,
|
|
118
|
-
letter;
|
|
119
|
-
for (let i = 0; i <= csvString.length; ++i) {
|
|
120
|
-
letter = csvString[i];
|
|
121
|
-
|
|
122
|
-
if ('"' === letter) {
|
|
123
|
-
if (canSplit && letter === prevLetter) {
|
|
124
|
-
row[columnIndex] += letter;
|
|
125
|
-
}
|
|
126
|
-
canSplit = !canSplit;
|
|
127
|
-
} else if ("," === letter && canSplit) {
|
|
128
|
-
letter = row[++columnIndex] = "";
|
|
129
|
-
} else if ("\n" === letter && canSplit) {
|
|
130
|
-
if ("\r" === prevLetter) {
|
|
131
|
-
row[columnIndex] = row[columnIndex].slice(0, -1);
|
|
132
|
-
}
|
|
133
|
-
row = result[++rowIndex] = [(letter = "")];
|
|
134
|
-
columnIndex = 0;
|
|
135
|
-
} else if (isString(letter)) {
|
|
136
|
-
row[columnIndex] += letter;
|
|
137
|
-
}
|
|
138
|
-
prevLetter = letter;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
result = result.filter((rowItem) => {
|
|
142
|
-
return rowItem[0] !== "";
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
return result;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
importable,
|
|
150
|
-
importSubmitUrl,
|
|
151
|
-
importParamName,
|
|
152
|
-
initCsvImport,
|
|
153
|
-
rowSelected,
|
|
154
|
-
selectedRowCount,
|
|
155
|
-
submitRows,
|
|
156
|
-
_submitEachRow: submitEachRow,
|
|
157
|
-
loadFile,
|
|
158
|
-
_parseCsv: parseCsv,
|
|
159
|
-
rowCheckId
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export { useTableImport };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { testPageUrl } from "../../helper.js";
|
|
2
|
-
|
|
3
|
-
const url = testPageUrl('lists_append');
|
|
4
|
-
|
|
5
|
-
describe('lists_append', () => {
|
|
6
|
-
it('appends rows to the populated list', () => {
|
|
7
|
-
cy.visit(url);
|
|
8
|
-
|
|
9
|
-
cy.contains('Starter row').should('exist');
|
|
10
|
-
|
|
11
|
-
cy.contains('lists/append (thumbnail)').click();
|
|
12
|
-
cy.get('#lists_append_status')
|
|
13
|
-
.should('contain.text', 'Status: appended');
|
|
14
|
-
cy.contains('New row').should('exist');
|
|
15
|
-
|
|
16
|
-
// cy.contains('Append standard row').click()
|
|
17
|
-
// cy.contains('Standard row').should('exist')
|
|
18
|
-
|
|
19
|
-
cy.contains('Append + snackbar').click();
|
|
20
|
-
cy.contains('Snackbar row').should('exist');
|
|
21
|
-
cy.contains('.v-snackbar', 'Row appended').should('exist');
|
|
22
|
-
|
|
23
|
-
cy.contains('Append two rows').click();
|
|
24
|
-
cy.contains('Batch row A').should('exist');
|
|
25
|
-
cy.contains('Batch row B').should('exist');
|
|
26
|
-
});
|
|
27
|
-
});
|