@emuanalytics/flow-rdf-ui 4.1.28 → 4.2.0
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/dist/components/calendar/calendar.component.mjs +534 -118
- package/dist/components/calendar/calendar.styles.mjs +142 -2
- package/dist/components/date-time-picker/date-time-picker.component.mjs +125 -9
- package/dist/components/query-builder/converters/flow-query-converters.mjs +21 -9
- package/dist/components/query-builder/converters/javascript-filter-converter.mjs +17 -1
- package/dist/components/query-builder/converters/map-layer-filter-converter.mjs +16 -1
- package/dist/components/query-builder/default-value-templates.mjs +68 -71
- package/dist/components/query-builder/helpers.mjs +18 -2
- package/dist/components/query-builder/query-builder.component.mjs +8 -3
- package/dist/custom-elements.json +6514 -5811
- package/dist/index.d.mts +239 -17
- package/dist/index.d.ts +239 -17
- package/dist/index.mjs +1 -1
- package/dist/internal/date-range.mjs +107 -0
- package/dist/internal/datetime.mjs +2 -13
- package/dist/package.json.mjs +1 -1
- package/dist/translations/en.mjs +5 -0
- package/dist/vscode.html-custom-data.json +302 -252
- package/package.json +3 -3
|
@@ -13,6 +13,9 @@ const styles = css`
|
|
|
13
13
|
--day-color-hover: var(--flow-ui-calendar-day-color-hover);
|
|
14
14
|
--day-color-selected: var(--flow-ui-calendar-day-color-selected);
|
|
15
15
|
--day-color-today: var(--flow-ui-calendar-day-color-today);
|
|
16
|
+
--day-color-in-range: var(--flow-ui-color-primary-100);
|
|
17
|
+
--day-gap: var(--flow-ui-spacing-2x-small);
|
|
18
|
+
--month-gap: var(--flow-ui-spacing-large);
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
.calendar {
|
|
@@ -43,21 +46,52 @@ const styles = css`
|
|
|
43
46
|
--day-font-size: var(--flow-ui-font-size-medium);
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
/* Two-month view: each month is --width wide */
|
|
50
|
+
.calendar--two-months {
|
|
51
|
+
width: calc(var(--width) * 2 + var(--month-gap));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.calendar__months {
|
|
55
|
+
flex: 1;
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: var(--month-gap);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.calendar__month {
|
|
61
|
+
flex: 1;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: var(--flow-ui-spacing-x-small);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* The months can have different numbers of weeks, so don't stretch the shorter one */
|
|
69
|
+
.calendar--two-months .calendar__months {
|
|
70
|
+
align-items: start;
|
|
71
|
+
}
|
|
72
|
+
|
|
46
73
|
.calendar__header {
|
|
47
74
|
display: flex;
|
|
48
75
|
align-items: center;
|
|
49
76
|
}
|
|
50
77
|
|
|
78
|
+
/* Keeps the space of the previous or next button that the other month has, so the month label stays centered */
|
|
79
|
+
.calendar__header-button--hidden {
|
|
80
|
+
visibility: hidden;
|
|
81
|
+
}
|
|
82
|
+
|
|
51
83
|
.calendar__header-month {
|
|
52
84
|
flex: 1;
|
|
53
85
|
text-align: center;
|
|
86
|
+
/* Match the month and year selects in the first month */
|
|
87
|
+
font-size: var(--flow-ui-control-font-size-medium);
|
|
54
88
|
}
|
|
55
89
|
|
|
56
90
|
.calendar__grid {
|
|
57
91
|
flex: 1;
|
|
58
92
|
display: grid;
|
|
59
93
|
grid-template-columns: repeat(7, 1fr);
|
|
60
|
-
gap: var(--
|
|
94
|
+
gap: var(--day-gap);
|
|
61
95
|
}
|
|
62
96
|
|
|
63
97
|
.calendar__header-day {
|
|
@@ -79,10 +113,22 @@ const styles = css`
|
|
|
79
113
|
font-size: var(--day-font-size);
|
|
80
114
|
}
|
|
81
115
|
|
|
82
|
-
.calendar__day:hover:not(
|
|
116
|
+
.calendar__day:hover:not(
|
|
117
|
+
.calendar__day--selected,
|
|
118
|
+
.calendar__day--range-start,
|
|
119
|
+
.calendar__day--range-last,
|
|
120
|
+
.calendar__day--disabled
|
|
121
|
+
) {
|
|
83
122
|
background-color: var(--day-color-hover);
|
|
84
123
|
}
|
|
85
124
|
|
|
125
|
+
/* Outside min or max, or too far from the pending day for max-range */
|
|
126
|
+
.calendar__day--disabled {
|
|
127
|
+
opacity: 0.4;
|
|
128
|
+
cursor: not-allowed;
|
|
129
|
+
text-decoration: line-through;
|
|
130
|
+
}
|
|
131
|
+
|
|
86
132
|
.calendar__day--filler {
|
|
87
133
|
color: var(--day-color-filler);
|
|
88
134
|
}
|
|
@@ -96,6 +142,71 @@ const styles = css`
|
|
|
96
142
|
background-color: var(--day-color-selected);
|
|
97
143
|
}
|
|
98
144
|
|
|
145
|
+
/*
|
|
146
|
+
* Date range. Each day in the range draws its part of the range band with a pseudo-element behind the day button, so
|
|
147
|
+
* the day's own background (the hover color, or the selected color at the ends of the range) is drawn on top. The
|
|
148
|
+
* band extends into the grid gap on both sides so that it is continuous across a week row, and has rounded ends at
|
|
149
|
+
* the start and end of each row.
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
.calendar__grid {
|
|
153
|
+
/* Keep the range band (z-index: -1) above the backgrounds of the calendar's ancestors */
|
|
154
|
+
isolation: isolate;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.calendar__day--in-range {
|
|
158
|
+
position: relative;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.calendar__day--in-range::before {
|
|
162
|
+
content: '';
|
|
163
|
+
position: absolute;
|
|
164
|
+
z-index: -1;
|
|
165
|
+
inset-block: 0;
|
|
166
|
+
inset-inline: calc(var(--day-gap) / -2);
|
|
167
|
+
background-color: var(--day-color-in-range);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.calendar__day--in-range.calendar__day--row-start::before {
|
|
171
|
+
inset-inline-start: 0;
|
|
172
|
+
border-start-start-radius: var(--flow-ui-border-radius-pill);
|
|
173
|
+
border-end-start-radius: var(--flow-ui-border-radius-pill);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.calendar__day--in-range.calendar__day--row-end::before {
|
|
177
|
+
inset-inline-end: 0;
|
|
178
|
+
border-start-end-radius: var(--flow-ui-border-radius-pill);
|
|
179
|
+
border-end-end-radius: var(--flow-ui-border-radius-pill);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* The band starts and ends at the center of the range's end days, under the selected color */
|
|
183
|
+
.calendar__day--in-range.calendar__day--range-start::before {
|
|
184
|
+
inset-inline-start: 50%;
|
|
185
|
+
border-start-start-radius: 0;
|
|
186
|
+
border-end-start-radius: 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.calendar__day--in-range.calendar__day--range-last::before {
|
|
190
|
+
inset-inline-end: 50%;
|
|
191
|
+
border-start-end-radius: 0;
|
|
192
|
+
border-end-end-radius: 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.calendar__day--range-start.calendar__day--range-last::before {
|
|
196
|
+
content: none;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* The filler color doesn't contrast enough with the range band, so mix it with the text color */
|
|
200
|
+
.calendar__day--filler.calendar__day--in-range:not(.calendar__day--range-start, .calendar__day--range-last) {
|
|
201
|
+
color: color-mix(in oklab, var(--day-color-filler), currentColor);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.calendar__day--range-start,
|
|
205
|
+
.calendar__day--range-last {
|
|
206
|
+
color: var(--flow-ui-color-background);
|
|
207
|
+
background-color: var(--day-color-selected);
|
|
208
|
+
}
|
|
209
|
+
|
|
99
210
|
.calendar__header {
|
|
100
211
|
justify-content: space-between;
|
|
101
212
|
padding-inline: var(--flow-ui-spacing-small);
|
|
@@ -111,6 +222,35 @@ const styles = css`
|
|
|
111
222
|
align-items: center;
|
|
112
223
|
}
|
|
113
224
|
|
|
225
|
+
/* The Start and End rows of a datetime range share one grid, so their labels and inputs line up */
|
|
226
|
+
.calendar__time-rows {
|
|
227
|
+
display: grid;
|
|
228
|
+
grid-template-columns: auto 1fr auto 1fr auto 1fr;
|
|
229
|
+
align-items: center;
|
|
230
|
+
gap: var(--flow-ui-spacing-2x-small);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.calendar__time-container--range {
|
|
234
|
+
display: contents;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Two-month datetime range: the Start row under the first month and the End row under the second */
|
|
238
|
+
.calendar__time-rows--split {
|
|
239
|
+
grid-template-columns: 1fr 1fr;
|
|
240
|
+
column-gap: var(--month-gap);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.calendar__time-rows--split .calendar__time-container--range {
|
|
244
|
+
display: flex;
|
|
245
|
+
min-width: 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.calendar__time-label {
|
|
249
|
+
padding-inline-end: var(--flow-ui-spacing-x-small);
|
|
250
|
+
font-size: var(--day-font-size);
|
|
251
|
+
font-weight: var(--flow-ui-font-weight-bold);
|
|
252
|
+
}
|
|
253
|
+
|
|
114
254
|
.calendar__time-input {
|
|
115
255
|
flex: 1;
|
|
116
256
|
/* Allow the input to shrink to accommodate narrow calendars in sidebars */
|
|
@@ -5,6 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
|
|
5
5
|
import { DateTime } from 'luxon';
|
|
6
6
|
import { stopAnimations, animateTo } from '../../internal/animate.mjs';
|
|
7
7
|
import { boolean } from '../../internal/boolean.mjs';
|
|
8
|
+
import { parseDateRange, lastDayOfRange, endTimeOfRange, formatEndOfDay, parseDateLimits, checkDateLimits } from '../../internal/date-range.mjs';
|
|
8
9
|
import { defaultValue } from '../../internal/default-value.mjs';
|
|
9
10
|
import { waitForEvent } from '../../internal/event.mjs';
|
|
10
11
|
import { FlowRdfFormControl } from '../../internal/flow-ui-rdf-element.mjs';
|
|
@@ -27,6 +28,11 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
27
28
|
__defProp(target, key, result);
|
|
28
29
|
return result;
|
|
29
30
|
};
|
|
31
|
+
const defaultFormats = {
|
|
32
|
+
date: "DD",
|
|
33
|
+
time: "HH:mm",
|
|
34
|
+
datetime: "FFF"
|
|
35
|
+
};
|
|
30
36
|
const popupOptions = {
|
|
31
37
|
small: { distance: -1, skidding: -4 },
|
|
32
38
|
medium: { distance: -3, skidding: -9 },
|
|
@@ -45,9 +51,10 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
45
51
|
this.name = "";
|
|
46
52
|
this.value = "";
|
|
47
53
|
this.displayMode = "datetime";
|
|
48
|
-
this.
|
|
54
|
+
this.range = false;
|
|
49
55
|
this.defaultValue = "";
|
|
50
56
|
this.size = "medium";
|
|
57
|
+
this.months = 1;
|
|
51
58
|
this.placeholder = "";
|
|
52
59
|
this.disabled = false;
|
|
53
60
|
this.clearable = false;
|
|
@@ -62,6 +69,16 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
62
69
|
this.required = false;
|
|
63
70
|
this.inline = false;
|
|
64
71
|
this.divider = false;
|
|
72
|
+
/** The message set with `setCustomValidity()`. It takes precedence over the limits messages. */
|
|
73
|
+
this.customValidityMessage = "";
|
|
74
|
+
}
|
|
75
|
+
get format() {
|
|
76
|
+
return this._format ?? defaultFormats[this.displayMode] ?? defaultFormats.datetime;
|
|
77
|
+
}
|
|
78
|
+
set format(format) {
|
|
79
|
+
const oldFormat = this._format;
|
|
80
|
+
this._format = isStringFull(format) ? format : void 0;
|
|
81
|
+
this.requestUpdate("format", oldFormat);
|
|
65
82
|
}
|
|
66
83
|
/** Gets the validity state object */
|
|
67
84
|
get validity() {
|
|
@@ -80,6 +97,7 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
80
97
|
this.filled = boolean(this.filled, true);
|
|
81
98
|
this.pill = boolean(this.pill, true);
|
|
82
99
|
this.inline = boolean(this.inline, true);
|
|
100
|
+
this.range = boolean(this.range, true);
|
|
83
101
|
this.handleDocumentFocusIn = this.handleDocumentFocusIn.bind(this);
|
|
84
102
|
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
|
|
85
103
|
this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
|
|
@@ -95,19 +113,48 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
95
113
|
document.removeEventListener("keydown", this.handleDocumentKeyDown);
|
|
96
114
|
document.removeEventListener("mousedown", this.handleDocumentMouseDown);
|
|
97
115
|
}
|
|
116
|
+
/** Whether the picker selects a range: a date range, a datetime range or a time range. */
|
|
117
|
+
get isRangeMode() {
|
|
118
|
+
return this.range;
|
|
119
|
+
}
|
|
98
120
|
updateDisplayLabel() {
|
|
99
121
|
try {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.displayLabel = DateTime.fromISO(this.value, { zone, locale }).toFormat(this.format);
|
|
104
|
-
} else {
|
|
122
|
+
const zone = isStringFull(this.timezone) ? this.timezone : void 0;
|
|
123
|
+
const locale = isStringFull(this.locale) ? this.locale : void 0;
|
|
124
|
+
if (!isStringFull(this.value)) {
|
|
105
125
|
this.displayLabel = "";
|
|
126
|
+
} else if (this.isRangeMode) {
|
|
127
|
+
this.displayLabel = this.rangeLabel(zone, locale);
|
|
128
|
+
} else {
|
|
129
|
+
this.displayLabel = DateTime.fromISO(this.value, { zone, locale }).toFormat(this.format);
|
|
106
130
|
}
|
|
107
131
|
} catch (e) {
|
|
108
132
|
console.warn(e.message);
|
|
109
133
|
}
|
|
110
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The label for a range. For a date range, it shows the range start and the last day, both included, and a range of
|
|
137
|
+
* one day shows that day once. For a datetime range, it shows the start and end instants. For a time range, it shows
|
|
138
|
+
* the start and end times, with an end at the end of the day shown as 24:00. An invalid range shows an empty label,
|
|
139
|
+
* as the calendar shows no selection.
|
|
140
|
+
*/
|
|
141
|
+
rangeLabel(zone, locale) {
|
|
142
|
+
const isDateRange = this.displayMode === "date";
|
|
143
|
+
const range = parseDateRange(this.value, { zone, locale, wholeDays: isDateRange });
|
|
144
|
+
if (!range) {
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
const first = range.start.toFormat(this.format);
|
|
148
|
+
let last;
|
|
149
|
+
if (isDateRange) {
|
|
150
|
+
last = lastDayOfRange(range).toFormat(this.format);
|
|
151
|
+
} else if (this.displayMode === "time" && endTimeOfRange(range).hour === 24) {
|
|
152
|
+
last = formatEndOfDay(lastDayOfRange(range), this.format);
|
|
153
|
+
} else {
|
|
154
|
+
last = range.end.toFormat(this.format);
|
|
155
|
+
}
|
|
156
|
+
return first === last ? first : `${first} \u2013 ${last}`;
|
|
157
|
+
}
|
|
111
158
|
handleFocus() {
|
|
112
159
|
this.hasFocus = true;
|
|
113
160
|
this.displayInput.setSelectionRange(0, 0);
|
|
@@ -133,10 +180,17 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
133
180
|
if (event.key === "Escape" && this.open) {
|
|
134
181
|
event.preventDefault();
|
|
135
182
|
event.stopPropagation();
|
|
183
|
+
const calendar = this.shadowRoot?.querySelector("flow-ui-calendar");
|
|
184
|
+
if (calendar?.cancelPendingRange()) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
136
187
|
this.hide();
|
|
137
188
|
this.displayInput.focus({ preventScroll: true });
|
|
138
189
|
}
|
|
139
190
|
if (event.key === "Enter") {
|
|
191
|
+
if (this.open && event.composedPath().includes(this.calendarContainer)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
140
194
|
event.preventDefault();
|
|
141
195
|
event.stopImmediatePropagation();
|
|
142
196
|
if (!this.open) {
|
|
@@ -185,6 +239,9 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
185
239
|
event.stopPropagation();
|
|
186
240
|
event.preventDefault();
|
|
187
241
|
}
|
|
242
|
+
handleCalendarInput(event) {
|
|
243
|
+
event.stopPropagation();
|
|
244
|
+
}
|
|
188
245
|
handleCalendarChange(event) {
|
|
189
246
|
event.stopPropagation();
|
|
190
247
|
const target = event.target;
|
|
@@ -196,6 +253,10 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
196
253
|
this.emit("flow-ui-change");
|
|
197
254
|
});
|
|
198
255
|
}
|
|
256
|
+
if (this.isRangeMode && this.displayMode === "date") {
|
|
257
|
+
this.hide();
|
|
258
|
+
this.displayInput.focus({ preventScroll: true });
|
|
259
|
+
}
|
|
199
260
|
}
|
|
200
261
|
handleInvalid(event) {
|
|
201
262
|
this.formControlController.setValidity(false);
|
|
@@ -262,9 +323,43 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
262
323
|
}
|
|
263
324
|
/** Sets a custom validation message. Pass an empty string to restore validity. */
|
|
264
325
|
setCustomValidity(message) {
|
|
265
|
-
this.
|
|
326
|
+
this.customValidityMessage = message;
|
|
327
|
+
this.updateCustomValidity();
|
|
328
|
+
}
|
|
329
|
+
updated(changedProperties) {
|
|
330
|
+
super.updated(changedProperties);
|
|
331
|
+
const limitProperties = ["value", "min", "max", "maxRange", "range", "displayMode", "timezone", "locale"];
|
|
332
|
+
if (limitProperties.some((name) => changedProperties.has(name))) {
|
|
333
|
+
this.updateCustomValidity();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** Set the value input's custom validity: the message from `setCustomValidity()`, or a limits message. */
|
|
337
|
+
updateCustomValidity() {
|
|
338
|
+
this.valueInput.setCustomValidity(this.customValidityMessage || this.limitsValidityMessage());
|
|
266
339
|
this.formControlController.updateValidity();
|
|
267
340
|
}
|
|
341
|
+
/** The validation message for a value outside `min`, `max` or `max-range`, or an empty string. */
|
|
342
|
+
limitsValidityMessage() {
|
|
343
|
+
if (this.displayMode === "time") {
|
|
344
|
+
return "";
|
|
345
|
+
}
|
|
346
|
+
const zone = isStringFull(this.timezone) ? this.timezone : void 0;
|
|
347
|
+
const locale = isStringFull(this.locale) ? this.locale : void 0;
|
|
348
|
+
const wholeDays = this.displayMode === "date";
|
|
349
|
+
const limits = parseDateLimits(this.min, this.max, this.maxRange, { zone, locale });
|
|
350
|
+
const violation = checkDateLimits(this.value, limits, { zone, locale, range: this.isRangeMode, wholeDays });
|
|
351
|
+
const formatLimit = (date) => date.toLocaleString(wholeDays ? DateTime.DATE_MED : DateTime.DATETIME_MED);
|
|
352
|
+
switch (violation) {
|
|
353
|
+
case "min":
|
|
354
|
+
return this.localize.term("dateBeforeMin", formatLimit(limits.min));
|
|
355
|
+
case "max":
|
|
356
|
+
return this.localize.term("dateAfterMax", formatLimit(limits.max));
|
|
357
|
+
case "maxRange":
|
|
358
|
+
return this.localize.term("dateRangeTooLong", limits.maxRange.toHuman());
|
|
359
|
+
default:
|
|
360
|
+
return "";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
268
363
|
/** Sets focus on the control. */
|
|
269
364
|
focus(options) {
|
|
270
365
|
this.displayInput.focus(options);
|
|
@@ -419,9 +514,15 @@ class FlowUiDateTimePicker extends FlowRdfFormControl {
|
|
|
419
514
|
<flow-ui-calendar
|
|
420
515
|
value=${this.value}
|
|
421
516
|
display-mode=${this.displayMode}
|
|
517
|
+
?range=${this.range}
|
|
518
|
+
min=${ifDefined(this.min)}
|
|
519
|
+
max=${ifDefined(this.max)}
|
|
520
|
+
max-range=${ifDefined(this.maxRange)}
|
|
422
521
|
size=${this.size}
|
|
522
|
+
months=${this.months}
|
|
423
523
|
timezone=${ifDefined(this.timezone)}
|
|
424
524
|
locale=${ifDefined(this.locale)}
|
|
525
|
+
@flow-ui-input=${this.handleCalendarInput}
|
|
425
526
|
@flow-ui-change=${this.handleCalendarChange}
|
|
426
527
|
></flow-ui-calendar>
|
|
427
528
|
` : nothing}
|
|
@@ -462,9 +563,12 @@ __decorateClass([
|
|
|
462
563
|
__decorateClass([
|
|
463
564
|
property({ attribute: "display-mode" })
|
|
464
565
|
], FlowUiDateTimePicker.prototype, "displayMode", 2);
|
|
566
|
+
__decorateClass([
|
|
567
|
+
property({ type: Boolean, reflect: true })
|
|
568
|
+
], FlowUiDateTimePicker.prototype, "range", 2);
|
|
465
569
|
__decorateClass([
|
|
466
570
|
property()
|
|
467
|
-
], FlowUiDateTimePicker.prototype, "format",
|
|
571
|
+
], FlowUiDateTimePicker.prototype, "format", 1);
|
|
468
572
|
__decorateClass([
|
|
469
573
|
property()
|
|
470
574
|
], FlowUiDateTimePicker.prototype, "timezone", 2);
|
|
@@ -477,6 +581,18 @@ __decorateClass([
|
|
|
477
581
|
__decorateClass([
|
|
478
582
|
property({ reflect: true })
|
|
479
583
|
], FlowUiDateTimePicker.prototype, "size", 2);
|
|
584
|
+
__decorateClass([
|
|
585
|
+
property({ type: Number })
|
|
586
|
+
], FlowUiDateTimePicker.prototype, "months", 2);
|
|
587
|
+
__decorateClass([
|
|
588
|
+
property()
|
|
589
|
+
], FlowUiDateTimePicker.prototype, "min", 2);
|
|
590
|
+
__decorateClass([
|
|
591
|
+
property()
|
|
592
|
+
], FlowUiDateTimePicker.prototype, "max", 2);
|
|
593
|
+
__decorateClass([
|
|
594
|
+
property({ attribute: "max-range" })
|
|
595
|
+
], FlowUiDateTimePicker.prototype, "maxRange", 2);
|
|
480
596
|
__decorateClass([
|
|
481
597
|
property()
|
|
482
598
|
], FlowUiDateTimePicker.prototype, "placeholder", 2);
|
|
@@ -520,7 +636,7 @@ __decorateClass([
|
|
|
520
636
|
property({ type: Boolean, reflect: true })
|
|
521
637
|
], FlowUiDateTimePicker.prototype, "divider", 2);
|
|
522
638
|
__decorateClass([
|
|
523
|
-
watch(["value", "format", "timezone", "locale"])
|
|
639
|
+
watch(["value", "format", "timezone", "locale", "range", "displayMode"])
|
|
524
640
|
], FlowUiDateTimePicker.prototype, "updateDisplayLabel", 1);
|
|
525
641
|
__decorateClass([
|
|
526
642
|
watch("disabled", { waitUntilFirstUpdate: true })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DateTime } from 'luxon';
|
|
2
2
|
import { isNil, isObjectFull, isFunction } from '../../../internal/type-checks.mjs';
|
|
3
3
|
import { escape } from '../../../utilities/regex.mjs';
|
|
4
|
-
import { filterRuleSet, isRule, isRuleSet } from '../helpers.mjs';
|
|
4
|
+
import { filterRuleSet, isRule, isRuleSet, betweenRuleValues } from '../helpers.mjs';
|
|
5
5
|
|
|
6
6
|
function isStringType(field) {
|
|
7
7
|
return field.type === "string";
|
|
@@ -24,6 +24,9 @@ function whereClausesFromRuleSet(rule, config, options) {
|
|
|
24
24
|
if (options?.renameFields && fieldName in options.renameFields) {
|
|
25
25
|
fieldName = options.renameFields[fieldName];
|
|
26
26
|
}
|
|
27
|
+
if (rule.operator === "between") {
|
|
28
|
+
return betweenClause(fieldName, rule, field, options);
|
|
29
|
+
}
|
|
27
30
|
let clause = ["is null", "not null"].includes(rule.operator) || !isNil(rule.value) ? { [fieldName]: predicateFromRule(rule, config) } : {};
|
|
28
31
|
if ((rule.operator === "!=" || rule.operator === "not like") && isObjectFull(clause)) {
|
|
29
32
|
clause = { $or: [{ [fieldName]: { $is: null } }, clause] };
|
|
@@ -35,6 +38,22 @@ function whereClausesFromRuleSet(rule, config, options) {
|
|
|
35
38
|
return rules.length ? { [condition]: rules } : {};
|
|
36
39
|
} else ;
|
|
37
40
|
}
|
|
41
|
+
function betweenClause(fieldName, rule, field, options) {
|
|
42
|
+
const values = betweenRuleValues(rule, field);
|
|
43
|
+
if (!values) {
|
|
44
|
+
const message = `flowQueryConverter: Skipping 'between' rule for field '${rule.field}': value must be [start, end]`;
|
|
45
|
+
if (options.throwOnError) {
|
|
46
|
+
throw new Error(message);
|
|
47
|
+
}
|
|
48
|
+
console.warn(message, rule);
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
const [start, end] = field.type === "datetime" ? values.map((v) => DateTime.fromISO(v).toISO()) : values;
|
|
52
|
+
if (end === void 0) {
|
|
53
|
+
return { [fieldName]: { $gte: start } };
|
|
54
|
+
}
|
|
55
|
+
return { $and: [{ [fieldName]: { $gte: start } }, { [fieldName]: { $lt: end } }] };
|
|
56
|
+
}
|
|
38
57
|
function predicateFromRule(rule, config) {
|
|
39
58
|
const fieldConfig = config.fields[rule.field];
|
|
40
59
|
const field = config.fields[rule.field];
|
|
@@ -105,18 +124,11 @@ function predicateFromRule(rule, config) {
|
|
|
105
124
|
operator = "$not";
|
|
106
125
|
value = null;
|
|
107
126
|
break;
|
|
108
|
-
case "between":
|
|
109
|
-
operator = "$between";
|
|
110
|
-
break;
|
|
111
127
|
default:
|
|
112
128
|
throw new Error(`Unrecognized query operator '${rule.operator}'`);
|
|
113
129
|
}
|
|
114
130
|
if (config.fields?.[rule.field]?.type === "datetime") {
|
|
115
|
-
|
|
116
|
-
value = [DateTime.fromISO(value[0] || 0).toISO(), DateTime.fromISO(value[1] || 0).toISO()];
|
|
117
|
-
} else {
|
|
118
|
-
value = DateTime.fromISO(value || 0).toISO();
|
|
119
|
-
}
|
|
131
|
+
value = DateTime.fromISO(value || 0).toISO();
|
|
120
132
|
}
|
|
121
133
|
return { [operator]: value };
|
|
122
134
|
}
|
|
@@ -2,7 +2,7 @@ import { DateTime } from 'luxon';
|
|
|
2
2
|
import { ensureArray } from '../../../internal/ensure-array.mjs';
|
|
3
3
|
import { isNil, isStringFull, isFunction } from '../../../internal/type-checks.mjs';
|
|
4
4
|
import { escape } from '../../../utilities/regex.mjs';
|
|
5
|
-
import { filterRuleSet, isRule, isRuleSet } from '../helpers.mjs';
|
|
5
|
+
import { filterRuleSet, isRule, isRuleSet, betweenRuleValues } from '../helpers.mjs';
|
|
6
6
|
|
|
7
7
|
class JavaScriptFilterError extends Error {
|
|
8
8
|
constructor(msg) {
|
|
@@ -62,6 +62,22 @@ function predicateFromRule(rule, config, options) {
|
|
|
62
62
|
let field = `get("${fieldName}", data)`;
|
|
63
63
|
let operator = "";
|
|
64
64
|
let value = rule.value;
|
|
65
|
+
if (rule.operator === "between") {
|
|
66
|
+
const values = betweenRuleValues(rule, fieldConfig);
|
|
67
|
+
if (!values) {
|
|
68
|
+
const message = `JavascriptFilterConverter: Skipping 'between' rule for field '${rule.field}': value must be [start, end]`;
|
|
69
|
+
if (options.throwOnError) {
|
|
70
|
+
throw new JavaScriptFilterError(message);
|
|
71
|
+
}
|
|
72
|
+
console.warn(message, rule);
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
const [start, end] = isDateType(fieldConfig) ? values.map((v) => v === void 0 ? v : convertDateTime(v, fieldConfig)) : values;
|
|
76
|
+
if (end === void 0) {
|
|
77
|
+
return `${field} >= ${JSON.stringify(start)}`;
|
|
78
|
+
}
|
|
79
|
+
return `(${field} >= ${JSON.stringify(start)}) && (${field} < ${JSON.stringify(end)})`;
|
|
80
|
+
}
|
|
65
81
|
if (isFunction(fieldConfig.convertValue)) {
|
|
66
82
|
value = fieldConfig.convertValue(value);
|
|
67
83
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ensureArray } from '../../../internal/ensure-array.mjs';
|
|
2
2
|
import { isNil, isStringFull, isFunction } from '../../../internal/type-checks.mjs';
|
|
3
3
|
import { escape } from '../../../utilities/regex.mjs';
|
|
4
|
-
import { filterRuleSet, isRule, isRuleSet } from '../helpers.mjs';
|
|
4
|
+
import { filterRuleSet, isRule, isRuleSet, betweenRuleValues } from '../helpers.mjs';
|
|
5
5
|
|
|
6
6
|
function isStringType(field) {
|
|
7
7
|
return field.type === "string";
|
|
@@ -42,6 +42,21 @@ function predicateFromRule(rule, config, options) {
|
|
|
42
42
|
let operator = "";
|
|
43
43
|
let value = rule.value;
|
|
44
44
|
let negate = false;
|
|
45
|
+
if (rule.operator === "between") {
|
|
46
|
+
const values = betweenRuleValues(rule, fieldConfig);
|
|
47
|
+
if (!values) {
|
|
48
|
+
const message = `mapLayerFilterConverter: Skipping 'between' rule for field '${rule.field}': value must be [start, end]`;
|
|
49
|
+
if (options.throwOnError) {
|
|
50
|
+
throw new Error(message);
|
|
51
|
+
}
|
|
52
|
+
console.warn(message, rule);
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
if (values[1] === void 0) {
|
|
56
|
+
return `${field} >= ${JSON.stringify(values[0])}`;
|
|
57
|
+
}
|
|
58
|
+
return `(${field} >= ${JSON.stringify(values[0])}) && (${field} < ${JSON.stringify(values[1])})`;
|
|
59
|
+
}
|
|
45
60
|
if (isFunction(fieldConfig.convertValue)) {
|
|
46
61
|
value = fieldConfig.convertValue(value);
|
|
47
62
|
}
|