glib-web 4.44.6 → 5.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/.github/workflows/lint.yml +2 -2
- package/.nycrc.json +3 -1
- package/README.md +9 -1
- package/agent/commands/generate_test.yaml +5 -5
- package/components/charts/series.js +23 -11
- package/components/component.vue +0 -5
- package/components/fields/checkGroup.vue +14 -7
- package/components/fields/richText2.vue +33 -3
- package/components/fields/upload.vue +5 -3
- package/components/mixins/styles.js +0 -1
- package/components/popover.vue +107 -78
- package/cypress/component/{inputUpload.cy.ts → inputUpload.cy.js} +3 -3
- package/cypress/component/{multiUpload.cy.ts → multiUpload.cy.js} +3 -3
- package/cypress/component/{placeholderUpload.cy.ts → placeholderUpload.cy.js} +3 -3
- package/cypress/component/{testUtils.ts → testUtils.js} +3 -15
- package/cypress/e2e/glib-web/{auth.cy.ts → auth.cy.js} +22 -1
- package/cypress/e2e/glib-web/{autoValidate.cy.ts → autoValidate.cy.js} +1 -1
- package/cypress/e2e/glib-web/{browsers.cy.ts → browsers.cy.js} +1 -1
- package/cypress/e2e/glib-web/{calendar.cy.ts → calendar.cy.js} +1 -1
- package/cypress/e2e/glib-web/{calendarEmptyData.cy.ts → calendarEmptyData.cy.js} +1 -1
- package/cypress/e2e/glib-web/{carousel.cy.ts → carousel.cy.js} +1 -1
- package/cypress/e2e/glib-web/{charts.cy.ts → charts.cy.js} +1 -1
- package/cypress/e2e/glib-web/{column.cy.ts → column.cy.js} +1 -1
- package/cypress/e2e/glib-web/{commands.cy.ts → commands.cy.js} +1 -1
- package/cypress/e2e/glib-web/{components.cy.ts → components.cy.js} +1 -1
- package/cypress/e2e/glib-web/{cookies.cy.ts → cookies.cy.js} +1 -1
- package/cypress/e2e/glib-web/{custom.cy.ts → custom.cy.js} +1 -1
- package/cypress/e2e/glib-web/dialog.cy.js +63 -0
- package/cypress/e2e/glib-web/{dialogOpen.cy.ts → dialogOpen.cy.js} +1 -1
- package/cypress/e2e/glib-web/{dirtyState.cy.ts → dirtyState.cy.js} +9 -24
- package/cypress/e2e/glib-web/{display.cy.ts → display.cy.js} +1 -1
- package/cypress/e2e/glib-web/{fields.cy.ts → fields.cy.js} +1 -1
- package/cypress/e2e/glib-web/{fieldsCaptcha.cy.ts → fieldsCaptcha.cy.js} +1 -1
- package/cypress/e2e/glib-web/fieldsCreditCard.cy.js +22 -0
- package/cypress/e2e/glib-web/fieldsDateTime.cy.js +48 -0
- package/cypress/e2e/glib-web/{fieldsDynamicSelect.cy.ts → fieldsDynamicSelect.cy.js} +1 -1
- package/cypress/e2e/glib-web/fieldsLocation.cy.js +40 -0
- package/cypress/e2e/glib-web/fieldsOtp.cy.js +68 -0
- package/cypress/e2e/glib-web/fieldsPhone.cy.js +87 -0
- package/cypress/e2e/glib-web/fieldsRating.cy.js +91 -0
- package/cypress/e2e/glib-web/fieldsRichText.cy.js +136 -0
- package/cypress/e2e/glib-web/{fieldsSelect.cy.ts → fieldsSelect.cy.js} +1 -1
- package/cypress/e2e/glib-web/{fieldsSign.cy.ts → fieldsSign.cy.js} +1 -1
- package/cypress/e2e/glib-web/fieldsStripeToken.cy.js +47 -0
- package/cypress/e2e/glib-web/{fieldsTimer.cy.ts → fieldsTimer.cy.js} +1 -1
- package/cypress/e2e/glib-web/fieldsUpload.cy.js +159 -0
- package/cypress/e2e/glib-web/{fieldsUrlFragment.cy.ts → fieldsUrlFragment.cy.js} +1 -1
- package/cypress/e2e/glib-web/{flow.cy.ts → flow.cy.js} +1 -1
- package/cypress/e2e/glib-web/{form.cy.ts → form.cy.js} +1 -1
- package/cypress/e2e/glib-web/{formDynamic.cy.ts → formDynamic.cy.js} +1 -1
- package/cypress/e2e/glib-web/{forms.cy.ts → forms.cy.js} +1 -1
- package/cypress/e2e/glib-web/{grid.cy.ts → grid.cy.js} +1 -1
- package/cypress/e2e/glib-web/{horizontal.cy.ts → horizontal.cy.js} +1 -1
- package/cypress/e2e/glib-web/{http.cy.ts → http.cy.js} +1 -1
- package/cypress/e2e/glib-web/{image.cy.ts → image.cy.js} +1 -1
- package/cypress/e2e/glib-web/{lifecycle.cy.ts → lifecycle.cy.js} +1 -1
- package/cypress/e2e/glib-web/{list.cy.ts → list.cy.js} +1 -1
- package/cypress/e2e/glib-web/{listEditable.cy.ts → listEditable.cy.js} +1 -1
- package/cypress/e2e/glib-web/{listsAppend.cy.ts → listsAppend.cy.js} +1 -1
- package/cypress/e2e/glib-web/{logicsSet.cy.ts → logicsSet.cy.js} +1 -1
- package/cypress/e2e/glib-web/{multimediaVideo.cy.ts → multimediaVideo.cy.js} +1 -1
- package/cypress/e2e/glib-web/{pagination.cy.ts → pagination.cy.js} +1 -1
- package/cypress/e2e/glib-web/{panels.cy.ts → panels.cy.js} +1 -1
- package/cypress/e2e/glib-web/{panelsBulkEdit2.cy.ts → panelsBulkEdit2.cy.js} +1 -1
- package/cypress/e2e/glib-web/{popovers.cy.ts → popovers.cy.js} +1 -1
- package/cypress/e2e/glib-web/{progressCircle.cy.ts → progressCircle.cy.js} +1 -1
- package/cypress/e2e/glib-web/{responsive.cy.ts → responsive.cy.js} +1 -1
- package/cypress/e2e/glib-web/{scroll.cy.ts → scroll.cy.js} +1 -1
- package/cypress/e2e/glib-web/{selectable.cy.ts → selectable.cy.js} +1 -1
- package/cypress/e2e/glib-web/{sheets.cy.ts → sheets.cy.js} +1 -1
- package/cypress/e2e/glib-web/{snackbars.cy.ts → snackbars.cy.js} +1 -1
- package/cypress/e2e/glib-web/{split.cy.ts → split.cy.js} +1 -1
- package/cypress/e2e/glib-web/{storageItems.cy.ts → storageItems.cy.js} +1 -1
- package/cypress/e2e/glib-web/{table.cy.ts → table.cy.js} +1 -1
- package/cypress/e2e/glib-web/{timeline.cy.ts → timeline.cy.js} +1 -1
- package/cypress/e2e/glib-web/{timeouts.cy.ts → timeouts.cy.js} +1 -1
- package/cypress/e2e/glib-web/{ul.cy.ts → ul.cy.js} +1 -1
- package/cypress/e2e/glib-web/{vertical.cy.ts → vertical.cy.js} +1 -1
- package/cypress/e2e/glib-web/{web.cy.ts → web.cy.js} +1 -1
- package/cypress/e2e/glib-web/window.cy.js +21 -0
- package/cypress/e2e/glib-web/{windows.cy.ts → windows.cy.js} +34 -2
- package/cypress/helper.js +19 -0
- package/cypress/support/{commands.ts → commands.js} +2 -2
- package/cypress/support/component.js +27 -0
- package/cypress/support/{e2e.ts → e2e.js} +18 -3
- package/{cypress.config.ts → cypress.config.js} +3 -2
- package/cypress.yml.example +6 -7
- package/doc/TESTING.md +2 -2
- package/package.json +1 -1
- package/components/composable/dropable.js +0 -52
- package/components/fields/googlePlace.vue +0 -162
- package/components/mixins/tooltip.js +0 -57
- package/cypress/e2e/glib-web/dialog.cy.ts +0 -25
- package/cypress/e2e/glib-web/fieldsUpload.cy.ts +0 -48
- package/cypress/e2e/glib-web/multiupload.cy.ts +0 -25
- package/cypress/e2e/glib-web/window.cy.ts +0 -14
- package/cypress/helper.ts +0 -7
- package/cypress/support/component.ts +0 -12
package/.nycrc.json
CHANGED
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- Edit settings.json
|
|
7
7
|
- `CTRL+SHIFT+P` -> `Preferences: Open User Settings (JSON)` -- This will open an editor for `settings.json`.
|
|
8
8
|
- Copy content of `settings.json.example` into the editor.
|
|
9
|
+
- Disable format-on-save for Volar to avoid formatting conflicts with ESLint.
|
|
9
10
|
|
|
10
11
|
## Set up Javascript CI
|
|
11
12
|
|
|
@@ -19,7 +20,7 @@ module.exports = defineConfig({
|
|
|
19
20
|
e2e: {
|
|
20
21
|
specPattern: 'node_modules/glib-web/cypress/e2e',
|
|
21
22
|
defaultBrowser: 'chrome',
|
|
22
|
-
supportFile: 'node_modules/glib-web/cypress/support/e2e.
|
|
23
|
+
supportFile: 'node_modules/glib-web/cypress/support/e2e.js'
|
|
23
24
|
},
|
|
24
25
|
});
|
|
25
26
|
```
|
|
@@ -29,6 +30,13 @@ module.exports = defineConfig({
|
|
|
29
30
|
- Run rails server `bin/rails s`
|
|
30
31
|
- Execute `yarn run cypress run`
|
|
31
32
|
|
|
33
|
+
## Run Cypress tests (glib-web-npm)
|
|
34
|
+
|
|
35
|
+
1. Clone and set up the backend repo (`glib-web`) because Cypress tests require matching `test_page/*` fixtures.
|
|
36
|
+
2. In `glib-web`, link this package: `yarn link glib-web`.
|
|
37
|
+
3. In project that use `glib-web`, start Rails: `bin/rails s` and `bin/vite dev` separately.
|
|
38
|
+
4. In this repo, run tests: `yarn test`.
|
|
39
|
+
|
|
32
40
|
## Best practices
|
|
33
41
|
|
|
34
42
|
- To prevent circular dependencies between components:
|
|
@@ -7,7 +7,7 @@ description: >
|
|
|
7
7
|
inputs:
|
|
8
8
|
doc_dir: doc/garage/test_page
|
|
9
9
|
test_dir: cypress/e2e/glib-web
|
|
10
|
-
test_helper: cypress/helper.
|
|
10
|
+
test_helper: cypress/helper.js
|
|
11
11
|
ai_test_prompt: |
|
|
12
12
|
Generate a Cypress e2e test for the glib test page: {{page_slug}}.
|
|
13
13
|
Inputs you can use:
|
|
@@ -79,10 +79,10 @@ steps:
|
|
|
79
79
|
|
|
80
80
|
- id: collect-test-page-slugs
|
|
81
81
|
instruction: |
|
|
82
|
-
Scan every *.cy.
|
|
82
|
+
Scan every *.cy.js under {{inputs.test_dir}} and extract all occurrences of
|
|
83
83
|
testPageUrl('...'). Collect the string values into test_page_slugs.
|
|
84
84
|
If a file contains no testPageUrl calls, derive a fallback slug by:
|
|
85
|
-
- Removing the .cy.
|
|
85
|
+
- Removing the .cy.js suffix.
|
|
86
86
|
- Converting camelCase to snake_case.
|
|
87
87
|
Output: test_page_slugs.
|
|
88
88
|
|
|
@@ -102,8 +102,8 @@ steps:
|
|
|
102
102
|
- id: scaffold-missing-tests
|
|
103
103
|
instruction: |
|
|
104
104
|
For each slug in missing_page_slugs:
|
|
105
|
-
1) Derive a filename by converting snake_case to camelCase and append ".cy.
|
|
106
|
-
Example: file_upload_new -> fileUploadNew.cy.
|
|
105
|
+
1) Derive a filename by converting snake_case to camelCase and append ".cy.js".
|
|
106
|
+
Example: file_upload_new -> fileUploadNew.cy.js
|
|
107
107
|
2) Create {{inputs.test_dir}}/<filename> if it does not already exist.
|
|
108
108
|
3) Write generated_tests_map[<slug>] to the file.
|
|
109
109
|
Output: created_test_files.
|
|
@@ -33,7 +33,7 @@ const installChartkick = () => {
|
|
|
33
33
|
const multipleDataSeries = (dataSeries) => {
|
|
34
34
|
return dataSeries.map((value) => {
|
|
35
35
|
let points = null;
|
|
36
|
-
if (
|
|
36
|
+
if (TypeUtils.isArray(value.points)) {
|
|
37
37
|
points = value.points.reduce((prev, curr) => {
|
|
38
38
|
return Object.assign(prev, { [curr.x]: curr.y });
|
|
39
39
|
}, {});
|
|
@@ -73,7 +73,11 @@ const getOrCreateTooltip = (chart) => {
|
|
|
73
73
|
|
|
74
74
|
const externalTooltipHandler = (multiple, dataSeries) => {
|
|
75
75
|
return (context) => {
|
|
76
|
-
const
|
|
76
|
+
const tooltipContext = TypeUtils.isObject(context) && TypeUtils.isObject(context.tooltip) ? context.tooltip : null;
|
|
77
|
+
const dataPoints = TypeUtils.isObject(tooltipContext) ? tooltipContext.dataPoints : null;
|
|
78
|
+
if (!TypeUtils.isArray(dataPoints) || dataPoints.length === 0) return;
|
|
79
|
+
|
|
80
|
+
const tooltipData = getData(multiple, dataSeries, dataPoints[0]);
|
|
77
81
|
|
|
78
82
|
if (!tooltipData) return;
|
|
79
83
|
|
|
@@ -111,9 +115,13 @@ const externalTooltipHandler = (multiple, dataSeries) => {
|
|
|
111
115
|
};
|
|
112
116
|
|
|
113
117
|
const getData = (multiple, dataSeries, context) => {
|
|
118
|
+
if (!TypeUtils.isArray(dataSeries) || !TypeUtils.isObject(context)) return null;
|
|
119
|
+
|
|
114
120
|
const { dataIndex, datasetIndex } = context;
|
|
121
|
+
if (!TypeUtils.isNumber(dataIndex) || !TypeUtils.isNumber(datasetIndex)) return null;
|
|
122
|
+
|
|
115
123
|
const dataset = dataSeries[datasetIndex];
|
|
116
|
-
if (multiple &&
|
|
124
|
+
if (multiple && TypeUtils.isObject(dataset) && TypeUtils.isArray(dataset.points) && dataset.points[dataIndex]) {
|
|
117
125
|
return dataset.points[dataIndex];
|
|
118
126
|
}
|
|
119
127
|
if (!multiple && dataSeries[dataIndex]) {
|
|
@@ -123,15 +131,16 @@ const getData = (multiple, dataSeries, context) => {
|
|
|
123
131
|
|
|
124
132
|
function useChart({ dataSeries, spec, multiple = true }) {
|
|
125
133
|
installChartkick();
|
|
134
|
+
const normalizedDataSeries = TypeUtils.isArray(dataSeries) ? dataSeries : [];
|
|
126
135
|
const isDonut = [spec.styleClasses].flat().includes('donut');
|
|
127
136
|
const { datalabels, centerLabel, customTooltip } = spec.plugins || {};
|
|
128
137
|
const legend = spec.legend || { display: true };
|
|
129
138
|
|
|
130
139
|
let series = null;
|
|
131
140
|
if (multiple) {
|
|
132
|
-
series = computed(() => multipleDataSeries(
|
|
141
|
+
series = computed(() => multipleDataSeries(normalizedDataSeries));
|
|
133
142
|
} else {
|
|
134
|
-
series = computed(() => singleDataSeries(
|
|
143
|
+
series = computed(() => singleDataSeries(normalizedDataSeries));
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
let colors = undefined;
|
|
@@ -175,12 +184,15 @@ function useChart({ dataSeries, spec, multiple = true }) {
|
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
const { dataIndex, datasetIndex } = context;
|
|
178
|
-
const dataset =
|
|
179
|
-
if (multiple &&
|
|
180
|
-
|
|
187
|
+
const dataset = normalizedDataSeries[datasetIndex];
|
|
188
|
+
if (multiple && TypeUtils.isObject(dataset) && TypeUtils.isArray(dataset.points)) {
|
|
189
|
+
const point = dataset.points[dataIndex];
|
|
190
|
+
if (TypeUtils.isObject(point) && point.label) {
|
|
191
|
+
val = point.label;
|
|
192
|
+
}
|
|
181
193
|
}
|
|
182
|
-
if (!multiple &&
|
|
183
|
-
val =
|
|
194
|
+
if (!multiple && TypeUtils.isObject(normalizedDataSeries[dataIndex]) && normalizedDataSeries[dataIndex].label) {
|
|
195
|
+
val = normalizedDataSeries[dataIndex].label;
|
|
184
196
|
}
|
|
185
197
|
|
|
186
198
|
return val;
|
|
@@ -195,7 +207,7 @@ function useChart({ dataSeries, spec, multiple = true }) {
|
|
|
195
207
|
if (customTooltip) {
|
|
196
208
|
options.plugins.tooltip = {
|
|
197
209
|
enabled: false,
|
|
198
|
-
external: externalTooltipHandler(multiple,
|
|
210
|
+
external: externalTooltipHandler(multiple, normalizedDataSeries)
|
|
199
211
|
};
|
|
200
212
|
}
|
|
201
213
|
|
package/components/component.vue
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
/> -->
|
|
16
16
|
|
|
17
17
|
<!-- <fields-latLong v-else-if="spec.view == 'fields/latLong-v1'" :spec="spec" /> -->
|
|
18
|
-
<fields-googlePlace v-else-if="spec.view == 'fields/googlePlace-v1'" ref="delegate" :spec="spec" />
|
|
19
18
|
|
|
20
19
|
<!-- <panels-responsive v-else-if="spec.view == 'panels/scroll-v1'" :spec="spec" /> -->
|
|
21
20
|
|
|
@@ -72,7 +71,6 @@ import TextAreaField from "./fields/textarea.vue";
|
|
|
72
71
|
const RichTextField2 = defineAsyncComponent(() => import("./fields/richText2.vue"));
|
|
73
72
|
// import NewRichTextField from "./fields/newRichText.vue";
|
|
74
73
|
const FileField = defineAsyncComponent(() => import("./fields/file.vue"));
|
|
75
|
-
const MultiUploadField = defineAsyncComponent(() => import("./fields/multiUpload.vue"));
|
|
76
74
|
const UploadField = defineAsyncComponent(() => import('./fields/upload.vue'));
|
|
77
75
|
import SignField from "./fields/sign.vue";
|
|
78
76
|
import SelectField from "./fields/_select.vue";
|
|
@@ -85,7 +83,6 @@ import CheckField from "./fields/check.vue";
|
|
|
85
83
|
import DateField from "./fields/date.vue";
|
|
86
84
|
import DateTimeField from "./fields/datetime.vue";
|
|
87
85
|
const LocationField = defineAsyncComponent(() => import("./fields/location.vue"));
|
|
88
|
-
import GooglePlaceField from "./fields/googlePlace.vue";
|
|
89
86
|
// import DynamicGroupField from "./fields/dynamicGroup.vue";
|
|
90
87
|
import DynamicGroupField from "./fields/dynamicGroup2.vue";
|
|
91
88
|
import StripeTokenField from "./fields/stripeToken.vue";
|
|
@@ -179,7 +176,6 @@ export default {
|
|
|
179
176
|
"fields-richText": RichTextField2,
|
|
180
177
|
// "fields-newRichText": NewRichTextField,
|
|
181
178
|
"fields-file": FileField,
|
|
182
|
-
"fields-multiUpload": MultiUploadField,
|
|
183
179
|
"fields-upload": UploadField,
|
|
184
180
|
"fields-sign": SignField,
|
|
185
181
|
"fields-select": SelectField,
|
|
@@ -192,7 +188,6 @@ export default {
|
|
|
192
188
|
"fields-date": DateField,
|
|
193
189
|
"fields-datetime": DateTimeField,
|
|
194
190
|
"fields-location": LocationField,
|
|
195
|
-
"fields-googlePlace": GooglePlaceField,
|
|
196
191
|
"fields-dynamicGroup": DynamicGroupField,
|
|
197
192
|
"fields-stripeToken": StripeTokenField,
|
|
198
193
|
"fields-stripeExternalAccount": StripeExternalAccount,
|
|
@@ -19,6 +19,14 @@ import GlibBase from "../base/glibBase.js";
|
|
|
19
19
|
import { defineComponent, ref, provide, toRef } from "vue";
|
|
20
20
|
import { useGlibInput } from "../composable/form";
|
|
21
21
|
import { useGlibSelectable, watchNoneOfAbove } from "../composable/selectable";
|
|
22
|
+
import { isArray, isNull, isString } from "../../utils/type";
|
|
23
|
+
|
|
24
|
+
const normalizeValue = (value) => {
|
|
25
|
+
if (isNull(value)) return [];
|
|
26
|
+
if (isArray(value)) return value;
|
|
27
|
+
if (isString(value)) return value.length > 0 ? [value] : [];
|
|
28
|
+
return [value];
|
|
29
|
+
};
|
|
22
30
|
|
|
23
31
|
export default defineComponent({
|
|
24
32
|
extends: GlibBase,
|
|
@@ -39,7 +47,7 @@ export default defineComponent({
|
|
|
39
47
|
const parentSpec = props.spec;
|
|
40
48
|
provide('parentSpec', parentSpec);
|
|
41
49
|
|
|
42
|
-
const fieldModel = toRef(props.spec.value);
|
|
50
|
+
const fieldModel = toRef(normalizeValue(props.spec.value));
|
|
43
51
|
provide('parentModel', fieldModel);
|
|
44
52
|
|
|
45
53
|
useGlibInput({ props });
|
|
@@ -60,18 +68,17 @@ export default defineComponent({
|
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
// watch(
|
|
63
|
-
// () => props.spec,
|
|
64
|
-
// (
|
|
65
|
-
// fieldModel.value =
|
|
66
|
-
// }
|
|
67
|
-
// { immediate: true, deep: true }
|
|
71
|
+
// () => props.spec.value,
|
|
72
|
+
// (value) => {
|
|
73
|
+
// fieldModel.value = normalizeValue(value);
|
|
74
|
+
// }
|
|
68
75
|
// );
|
|
69
76
|
|
|
70
77
|
return { options, fieldModel, checkAll };
|
|
71
78
|
},
|
|
72
79
|
computed: {
|
|
73
80
|
values() {
|
|
74
|
-
return this.fieldModel
|
|
81
|
+
return normalizeValue(this.fieldModel);
|
|
75
82
|
}
|
|
76
83
|
},
|
|
77
84
|
watch: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="loadIf" :style="$styles()" :class="$classes()">
|
|
2
|
+
<div v-if="loadIf" :style="$styles()" :class="[$classes(), { 'glib-quill-disabled': inputDisabled }]">
|
|
3
3
|
<div ref="editor"></div>
|
|
4
4
|
<input type="hidden" :name="fieldName" :value="producedValue" />
|
|
5
5
|
<template v-if="Object.keys(files).length > 0">
|
|
@@ -51,6 +51,7 @@ import { useFileUtils } from "../composable/file";
|
|
|
51
51
|
import { setBusyWhenUploading, uploadFiles } from "../composable/upload";
|
|
52
52
|
import dom from "../../utils/dom";
|
|
53
53
|
import RawText from "./rawText.vue";
|
|
54
|
+
import { isBoolean } from "../../utils/type";
|
|
54
55
|
|
|
55
56
|
const { Item, makeKey } = useFileUtils();
|
|
56
57
|
|
|
@@ -74,6 +75,15 @@ export default defineComponent({
|
|
|
74
75
|
const files = ref({});
|
|
75
76
|
setBusyWhenUploading({ files });
|
|
76
77
|
|
|
78
|
+
const inputDisabled = computed(() => {
|
|
79
|
+
const disabled = instance.proxy.inputDisabled;
|
|
80
|
+
return isBoolean(disabled) ? disabled : false;
|
|
81
|
+
});
|
|
82
|
+
const isReadOnly = computed(() => {
|
|
83
|
+
const readOnly = props.spec.readOnly;
|
|
84
|
+
return (isBoolean(readOnly) ? readOnly : false) || inputDisabled.value;
|
|
85
|
+
});
|
|
86
|
+
|
|
77
87
|
|
|
78
88
|
function sanitizedValue() {
|
|
79
89
|
if (!quill) return props.spec.value || '';
|
|
@@ -254,11 +264,12 @@ export default defineComponent({
|
|
|
254
264
|
}
|
|
255
265
|
onMounted(() => {
|
|
256
266
|
quill = new Quill(editor.value, {
|
|
257
|
-
readOnly:
|
|
267
|
+
readOnly: isReadOnly.value,
|
|
258
268
|
placeholder: props.spec.placeholder,
|
|
259
269
|
theme: 'snow',
|
|
260
270
|
modules
|
|
261
271
|
});
|
|
272
|
+
quill.enable(!isReadOnly.value);
|
|
262
273
|
|
|
263
274
|
let value = props.spec.value;
|
|
264
275
|
switch (accept.value) {
|
|
@@ -296,7 +307,12 @@ export default defineComponent({
|
|
|
296
307
|
}
|
|
297
308
|
});
|
|
298
309
|
|
|
299
|
-
|
|
310
|
+
watch(isReadOnly, (readOnly) => {
|
|
311
|
+
if (!quill) return;
|
|
312
|
+
quill.enable(!readOnly);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return { producedValue, editor, files, rawTextProps, inputDisabled };
|
|
300
316
|
}
|
|
301
317
|
});
|
|
302
318
|
</script>
|
|
@@ -344,4 +360,18 @@ export default defineComponent({
|
|
|
344
360
|
max-height: 300px;
|
|
345
361
|
overflow-y: auto;
|
|
346
362
|
}
|
|
363
|
+
|
|
364
|
+
.glib-quill-disabled .ql-editor {
|
|
365
|
+
caret-color: transparent;
|
|
366
|
+
cursor: not-allowed;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.glib-quill-disabled .ql-toolbar {
|
|
370
|
+
opacity: var(--v-disabled-opacity);
|
|
371
|
+
cursor: not-allowed;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.glib-quill-disabled .ql-container {
|
|
375
|
+
opacity: var(--v-disabled-opacity);
|
|
376
|
+
}
|
|
347
377
|
</style>
|
|
@@ -29,9 +29,11 @@ const childSpec = computed(() => {
|
|
|
29
29
|
return Object.assign({}, props.spec, { placeholderView: null }, props.spec.placeholderView);
|
|
30
30
|
} else if (props.spec.inputView) {
|
|
31
31
|
return Object.assign({}, props.spec, { inputView: null }, props.spec.inputView);
|
|
32
|
-
} else {
|
|
33
|
-
return Object.assign({}, props.spec, { multiProgressView: null }, props.spec.
|
|
32
|
+
} else if (props.spec.multiProgressView) {
|
|
33
|
+
return Object.assign({}, props.spec, { multiProgressView: null }, props.spec.multiProgressView);
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
return props.spec;
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
function trigger() {
|
|
@@ -43,4 +45,4 @@ function reset() {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
defineExpose({trigger, reset})
|
|
46
|
-
</script>
|
|
48
|
+
</script>
|
|
@@ -131,7 +131,6 @@ export default {
|
|
|
131
131
|
const forceUpdate = (Math.random() + 1).toString(36).substring(7);
|
|
132
132
|
if (item.view.startsWith('charts/')) return forceUpdate;
|
|
133
133
|
if (item.view.startsWith('fields/richText')) return forceUpdate;
|
|
134
|
-
if (item.view.startsWith('fields/multiUpload')) return forceUpdate;
|
|
135
134
|
if (item.view.startsWith('panels/custom')) return forceUpdate;
|
|
136
135
|
|
|
137
136
|
const childViewLength = item.childViews ? item.childViews.length : 0;
|
package/components/popover.vue
CHANGED
|
@@ -1,100 +1,129 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
<!-- Placement options:
|
|
3
|
+
| 'top'
|
|
4
|
+
| 'top-start'
|
|
5
|
+
| 'top-end'
|
|
6
|
+
| 'right'
|
|
7
|
+
| 'right-start'
|
|
8
|
+
| 'right-end'
|
|
9
|
+
| 'bottom'
|
|
10
|
+
| 'bottom-start'
|
|
11
|
+
| 'bottom-end'
|
|
12
|
+
| 'left'
|
|
13
|
+
| 'left-start'
|
|
14
|
+
| 'left-end'
|
|
15
15
|
-->
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
<common-responsive v-if="toggle" :key="key" ref="container" :class="`views-popovers ${spec.styleClass}`"
|
|
18
|
+
:spec="popoverBody" />
|
|
20
19
|
</template>
|
|
21
20
|
|
|
22
21
|
<script>
|
|
23
22
|
import { popovers } from "../store";
|
|
24
23
|
import bus from "../utils/eventBus";
|
|
25
24
|
import { driver } from "driver.js";
|
|
26
|
-
import
|
|
25
|
+
import "driver.js/dist/driver.css";
|
|
27
26
|
import { strandom } from "./helper";
|
|
28
27
|
import { APP_ID } from "..";
|
|
29
28
|
import GlibBase from "./base/glibBase.js";
|
|
29
|
+
import * as TypeUtils from "../utils/type";
|
|
30
30
|
|
|
31
31
|
export default {
|
|
32
32
|
extends: GlibBase,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
props: ["spec", "reference", "placeholder", "styleClass"],
|
|
34
|
+
data() {
|
|
35
|
+
const allowClose = this.spec.overlay ? this.spec.overlay.closeOnFocus : true;
|
|
36
|
+
return {
|
|
37
|
+
key: null,
|
|
38
|
+
toggle: true,
|
|
39
|
+
driverObj: driver({
|
|
40
|
+
allowClose,
|
|
41
|
+
onDestroyed: () => {
|
|
42
|
+
this.close();
|
|
43
|
+
}
|
|
44
|
+
}),
|
|
45
|
+
busOpenHandler: null,
|
|
46
|
+
busCloseHandler: null,
|
|
47
|
+
onAppClick: null,
|
|
48
|
+
onResize: null,
|
|
49
|
+
body: null,
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
computed: {
|
|
53
|
+
popoverBody() {
|
|
54
|
+
if (TypeUtils.isNotNull(this.body)) {
|
|
55
|
+
return this.body;
|
|
56
|
+
}
|
|
57
|
+
return this.spec.body;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
mounted() {
|
|
61
|
+
popovers.value.push(this);
|
|
62
|
+
this.busOpenHandler = (body) => {
|
|
63
|
+
this.body = body;
|
|
64
|
+
this.key = strandom();
|
|
65
|
+
};
|
|
66
|
+
bus.$on(`popovers/open-${this.spec.key}`, this.busOpenHandler);
|
|
67
|
+
},
|
|
68
|
+
methods: {
|
|
69
|
+
$mounted() {
|
|
70
|
+
const appEl = document.getElementById(APP_ID);
|
|
71
|
+
this.busCloseHandler = () => {
|
|
72
|
+
if (appEl) {
|
|
73
|
+
appEl.removeEventListener("click", this.onAppClick);
|
|
74
|
+
}
|
|
75
|
+
this.close();
|
|
76
|
+
};
|
|
77
|
+
bus.$once(`popover/close-${this.spec.key}`, this.busCloseHandler);
|
|
78
|
+
if (!this.spec.persistent) {
|
|
79
|
+
this.onAppClick = this.handleClose;
|
|
80
|
+
if (appEl) {
|
|
81
|
+
appEl.addEventListener("click", this.onAppClick);
|
|
82
|
+
}
|
|
83
|
+
this.onResize = () => this.close();
|
|
84
|
+
window.addEventListener("resize", this.onResize);
|
|
85
|
+
}
|
|
86
|
+
if (this.spec.overlay) {
|
|
87
|
+
this.driverObj.highlight({ element: this.reference });
|
|
88
|
+
}
|
|
46
89
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
bus.$
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
90
|
+
$tearDown() {
|
|
91
|
+
if (TypeUtils.isFunction(this.busCloseHandler)) {
|
|
92
|
+
bus.$off(`popover/close-${this.spec.key}`, this.busCloseHandler);
|
|
93
|
+
}
|
|
94
|
+
if (TypeUtils.isFunction(this.busOpenHandler)) {
|
|
95
|
+
bus.$off(`popovers/open-${this.spec.key}`, this.busOpenHandler);
|
|
96
|
+
}
|
|
97
|
+
if (!this.spec.persistent) {
|
|
98
|
+
const appEl = document.getElementById(APP_ID);
|
|
99
|
+
if (TypeUtils.isFunction(this.onAppClick) && appEl) {
|
|
100
|
+
appEl.removeEventListener("click", this.onAppClick);
|
|
101
|
+
}
|
|
102
|
+
if (TypeUtils.isFunction(this.onResize)) {
|
|
103
|
+
window.removeEventListener("resize", this.onResize);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
53
106
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
appEl.addEventListener('click', this.handleClose);
|
|
63
|
-
window.addEventListener('resize', () => this.close());
|
|
64
|
-
}
|
|
65
|
-
if (this.spec.overlay) {
|
|
66
|
-
this.driverObj.highlight({ element: this.reference });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
$tearDown() {
|
|
71
|
-
bus.$off(`popover/close-${this.spec.key}`);
|
|
72
|
-
bus.$off(`popover/open-${this.spec.key}`);
|
|
73
|
-
if (!this.spec.persistent) {
|
|
74
|
-
appEl.removeEventListener('click', this.handleClose);
|
|
75
|
-
window.removeEventListener('resize', () => this.close());
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
handleClose(event) {
|
|
79
|
-
let element = null;
|
|
80
|
-
if (this.$refs.container) {
|
|
81
|
-
element = this.$refs.container.$el;
|
|
82
|
-
} else {
|
|
83
|
-
element = this.$el;
|
|
84
|
-
}
|
|
85
|
-
const isClickInside = element.contains(event.target);
|
|
107
|
+
handleClose(event) {
|
|
108
|
+
let element = null;
|
|
109
|
+
if (this.$refs.container) {
|
|
110
|
+
element = this.$refs.container.$el;
|
|
111
|
+
} else {
|
|
112
|
+
element = this.$el;
|
|
113
|
+
}
|
|
114
|
+
const isClickInside = element.contains(event.target);
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
116
|
+
if (!isClickInside) {
|
|
117
|
+
this.close();
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
close() {
|
|
121
|
+
if (this.placeholder) this.placeholder.remove();
|
|
122
|
+
if (this.driverObj) this.driverObj.destroy();
|
|
123
|
+
this.toggle = false;
|
|
124
|
+
popovers.value.remove(this);
|
|
97
125
|
}
|
|
126
|
+
}
|
|
98
127
|
};
|
|
99
128
|
</script>
|
|
100
129
|
|
|
@@ -2,14 +2,14 @@ import inputUpload from "../../components/fields/inputUpload.vue";
|
|
|
2
2
|
import Uploader from "../../utils/glibDirectUpload";
|
|
3
3
|
import { mountWithGlib, restoreUploaderStartStub, setupGlibGlobals } from "./testUtils";
|
|
4
4
|
|
|
5
|
-
const buildSpec = (overrides
|
|
5
|
+
const buildSpec = (overrides = {}) => ({
|
|
6
6
|
name: "user[file]",
|
|
7
7
|
label: "Upload file",
|
|
8
8
|
accepts: { fileType: "txt" },
|
|
9
9
|
...overrides,
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
const mountComponent = (spec
|
|
12
|
+
const mountComponent = (spec) => {
|
|
13
13
|
mountWithGlib(inputUpload, spec);
|
|
14
14
|
};
|
|
15
15
|
|
|
@@ -24,7 +24,7 @@ describe("inputUpload", () => {
|
|
|
24
24
|
|
|
25
25
|
it("uploads via direct upload and sets hidden signed ids", () => {
|
|
26
26
|
cy.stub(Uploader.prototype, "start")
|
|
27
|
-
.callsFake((callback
|
|
27
|
+
.callsFake((callback) => {
|
|
28
28
|
callback(null, { signed_id: "signed-123" });
|
|
29
29
|
})
|
|
30
30
|
.as("directUploadStart");
|
|
@@ -2,7 +2,7 @@ import multiUpload from "../../components/fields/multiUpload.vue";
|
|
|
2
2
|
import Uploader from "../../utils/glibDirectUpload";
|
|
3
3
|
import { mountWithGlib, restoreUploaderStartStub, setupGlibGlobals } from "./testUtils";
|
|
4
4
|
|
|
5
|
-
const buildSpec = (overrides
|
|
5
|
+
const buildSpec = (overrides = {}) => ({
|
|
6
6
|
name: "user[files][]",
|
|
7
7
|
accepts: { fileType: "txt" },
|
|
8
8
|
placeholder: "Drop files",
|
|
@@ -10,7 +10,7 @@ const buildSpec = (overrides: Record<string, unknown> = {}) => ({
|
|
|
10
10
|
...overrides,
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
const mountComponent = (spec
|
|
13
|
+
const mountComponent = (spec) => {
|
|
14
14
|
mountWithGlib(multiUpload, spec);
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@ describe("multiUpload", () => {
|
|
|
25
25
|
|
|
26
26
|
it("uploads via direct upload and sets hidden signed ids", () => {
|
|
27
27
|
cy.stub(Uploader.prototype, "start")
|
|
28
|
-
.callsFake((callback
|
|
28
|
+
.callsFake((callback) => {
|
|
29
29
|
callback(null, { signed_id: "signed-123" });
|
|
30
30
|
})
|
|
31
31
|
.as("directUploadStart");
|
|
@@ -2,7 +2,7 @@ import placeholderUpload from "../../components/fields/placeholderUpload.vue";
|
|
|
2
2
|
import Uploader from "../../utils/glibDirectUpload";
|
|
3
3
|
import { mountWithGlib, restoreUploaderStartStub, setupGlibGlobals } from "./testUtils";
|
|
4
4
|
|
|
5
|
-
const buildSpec = (overrides
|
|
5
|
+
const buildSpec = (overrides = {}) => ({
|
|
6
6
|
name: "user[file]",
|
|
7
7
|
type: "image",
|
|
8
8
|
width: 120,
|
|
@@ -12,7 +12,7 @@ const buildSpec = (overrides: Record<string, unknown> = {}) => ({
|
|
|
12
12
|
...overrides,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
const mountComponent = (spec
|
|
15
|
+
const mountComponent = (spec) => {
|
|
16
16
|
mountWithGlib(placeholderUpload, spec);
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -27,7 +27,7 @@ describe("placeholderUpload", () => {
|
|
|
27
27
|
|
|
28
28
|
it("uploads via direct upload and sets hidden signed id", () => {
|
|
29
29
|
cy.stub(Uploader.prototype, "start")
|
|
30
|
-
.callsFake((callback
|
|
30
|
+
.callsFake((callback) => {
|
|
31
31
|
callback(null, { signed_id: "signed-123" });
|
|
32
32
|
})
|
|
33
33
|
.as("directUploadStart");
|