@bpmn-io/form-js-viewer 0.7.2 → 0.8.0-alpha.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/assets/form-js.css +127 -24
- package/dist/index.cjs +510 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +507 -43
- package/dist/index.es.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/render/components/form-fields/Checklist.d.ts +10 -0
- package/dist/types/render/components/form-fields/Radio.d.ts +1 -6
- package/dist/types/render/components/form-fields/Select.d.ts +1 -6
- package/dist/types/render/components/form-fields/Taglist.d.ts +10 -0
- package/dist/types/render/components/form-fields/parts/DropdownList.d.ts +1 -0
- package/dist/types/render/components/index.d.ts +3 -1
- package/dist/types/render/hooks/useKeyDownAction.d.ts +1 -0
- package/dist/types/render/hooks/useValuesAsync.d.ts +28 -0
- package/package.json +4 -3
package/dist/index.es.js
CHANGED
|
@@ -2,10 +2,11 @@ import Ids from 'ids';
|
|
|
2
2
|
import { isArray, isFunction, isNumber, bind, assign, isNil, get, isUndefined, set, isString } from 'min-dash';
|
|
3
3
|
import snarkdown from '@bpmn-io/snarkdown';
|
|
4
4
|
import { jsx, jsxs } from 'preact/jsx-runtime';
|
|
5
|
-
import { useContext, useState, useCallback } from 'preact/hooks';
|
|
5
|
+
import { useContext, useState, useEffect, useRef, useMemo, useCallback } from 'preact/hooks';
|
|
6
6
|
import { createContext, createElement, Fragment, render } from 'preact';
|
|
7
|
+
import React, { createPortal } from 'preact/compat';
|
|
8
|
+
import classNames from 'classnames';
|
|
7
9
|
import Markup from 'preact-markup';
|
|
8
|
-
import { createPortal } from 'preact/compat';
|
|
9
10
|
import { Injector } from 'didi';
|
|
10
11
|
|
|
11
12
|
var FN_REF = '__fn';
|
|
@@ -807,19 +808,26 @@ class Importer {
|
|
|
807
808
|
const {
|
|
808
809
|
defaultValue,
|
|
809
810
|
_path,
|
|
810
|
-
type
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
811
|
+
type,
|
|
812
|
+
valuesKey
|
|
813
|
+
} = formField; // get values defined via valuesKey
|
|
814
|
+
|
|
815
|
+
if (valuesKey) {
|
|
816
|
+
importedData = { ...importedData,
|
|
817
|
+
[valuesKey]: get(data, [valuesKey])
|
|
818
|
+
};
|
|
819
|
+
} // try to get value from data
|
|
820
|
+
// if unavailable - try to get default value from form field
|
|
821
|
+
// if unavailable - get empty value from form field
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
if (_path) {
|
|
825
|
+
importedData = { ...importedData,
|
|
826
|
+
[_path[0]]: get(data, _path, isUndefined(defaultValue) ? this._formFields.get(type).emptyValue : defaultValue)
|
|
827
|
+
};
|
|
828
|
+
}
|
|
819
829
|
|
|
820
|
-
return
|
|
821
|
-
[_path[0]]: get(data, _path, isUndefined(defaultValue) ? this._formFields.get(type).emptyValue : defaultValue)
|
|
822
|
-
};
|
|
830
|
+
return importedData;
|
|
823
831
|
}, {});
|
|
824
832
|
}
|
|
825
833
|
|
|
@@ -980,7 +988,7 @@ function safeMarkdown(markdown) {
|
|
|
980
988
|
return sanitizeHTML(html);
|
|
981
989
|
}
|
|
982
990
|
|
|
983
|
-
const type$
|
|
991
|
+
const type$8 = 'button';
|
|
984
992
|
function Button(props) {
|
|
985
993
|
const {
|
|
986
994
|
disabled,
|
|
@@ -990,7 +998,7 @@ function Button(props) {
|
|
|
990
998
|
action = 'submit'
|
|
991
999
|
} = field;
|
|
992
1000
|
return jsx("div", {
|
|
993
|
-
class: formFieldClasses(type$
|
|
1001
|
+
class: formFieldClasses(type$8),
|
|
994
1002
|
children: jsx("button", {
|
|
995
1003
|
class: "fjs-button",
|
|
996
1004
|
type: action,
|
|
@@ -1007,7 +1015,7 @@ Button.create = function (options = {}) {
|
|
|
1007
1015
|
};
|
|
1008
1016
|
};
|
|
1009
1017
|
|
|
1010
|
-
Button.type = type$
|
|
1018
|
+
Button.type = type$8;
|
|
1011
1019
|
Button.label = 'Button';
|
|
1012
1020
|
Button.keyed = true;
|
|
1013
1021
|
|
|
@@ -1089,7 +1097,7 @@ function Label(props) {
|
|
|
1089
1097
|
});
|
|
1090
1098
|
}
|
|
1091
1099
|
|
|
1092
|
-
const type$
|
|
1100
|
+
const type$7 = 'checkbox';
|
|
1093
1101
|
function Checkbox(props) {
|
|
1094
1102
|
const {
|
|
1095
1103
|
disabled,
|
|
@@ -1116,7 +1124,7 @@ function Checkbox(props) {
|
|
|
1116
1124
|
formId
|
|
1117
1125
|
} = useContext(FormContext);
|
|
1118
1126
|
return jsxs("div", {
|
|
1119
|
-
class: formFieldClasses(type$
|
|
1127
|
+
class: formFieldClasses(type$7, errors),
|
|
1120
1128
|
children: [jsx(Label, {
|
|
1121
1129
|
id: prefixId(id, formId),
|
|
1122
1130
|
label: label,
|
|
@@ -1142,7 +1150,7 @@ Checkbox.create = function (options = {}) {
|
|
|
1142
1150
|
};
|
|
1143
1151
|
};
|
|
1144
1152
|
|
|
1145
|
-
Checkbox.type = type$
|
|
1153
|
+
Checkbox.type = type$7;
|
|
1146
1154
|
Checkbox.label = 'Checkbox';
|
|
1147
1155
|
Checkbox.keyed = true;
|
|
1148
1156
|
Checkbox.emptyValue = false;
|
|
@@ -1154,6 +1162,152 @@ function useService (type, strict) {
|
|
|
1154
1162
|
return getService(type, strict);
|
|
1155
1163
|
}
|
|
1156
1164
|
|
|
1165
|
+
/**
|
|
1166
|
+
* @enum { String }
|
|
1167
|
+
*/
|
|
1168
|
+
|
|
1169
|
+
const LOAD_STATES = {
|
|
1170
|
+
LOADING: 'loading',
|
|
1171
|
+
LOADED: 'loaded',
|
|
1172
|
+
ERROR: 'error'
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* @typedef {Object} ValuesGetter
|
|
1176
|
+
* @property {Object[]} values - The values data
|
|
1177
|
+
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
1178
|
+
*/
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* A hook to load values for single and multiselect components.
|
|
1182
|
+
*
|
|
1183
|
+
* @param {Object} field - The form field to handle values for
|
|
1184
|
+
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
1185
|
+
*/
|
|
1186
|
+
|
|
1187
|
+
function useValuesAsync (field) {
|
|
1188
|
+
const {
|
|
1189
|
+
valuesKey,
|
|
1190
|
+
values: staticValues
|
|
1191
|
+
} = field;
|
|
1192
|
+
const [valuesGetter, setValuesGetter] = useState({
|
|
1193
|
+
values: [],
|
|
1194
|
+
error: undefined,
|
|
1195
|
+
state: LOAD_STATES.LOADING
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
const initialData = useService('form')._getState().initialData;
|
|
1199
|
+
|
|
1200
|
+
useEffect(() => {
|
|
1201
|
+
let values = [];
|
|
1202
|
+
|
|
1203
|
+
if (valuesKey !== undefined) {
|
|
1204
|
+
const keyedValues = (initialData || {})[valuesKey];
|
|
1205
|
+
|
|
1206
|
+
if (keyedValues && Array.isArray(keyedValues)) {
|
|
1207
|
+
values = keyedValues;
|
|
1208
|
+
}
|
|
1209
|
+
} else if (staticValues !== undefined) {
|
|
1210
|
+
values = Array.isArray(staticValues) ? staticValues : [];
|
|
1211
|
+
} else {
|
|
1212
|
+
setValuesGetter(getErrorState('No values source defined in the form definition'));
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
setValuesGetter(buildLoadedState(values));
|
|
1217
|
+
}, [valuesKey, staticValues, initialData]);
|
|
1218
|
+
return valuesGetter;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
const getErrorState = error => ({
|
|
1222
|
+
values: [],
|
|
1223
|
+
error,
|
|
1224
|
+
state: LOAD_STATES.ERROR
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
const buildLoadedState = values => ({
|
|
1228
|
+
values,
|
|
1229
|
+
error: undefined,
|
|
1230
|
+
state: LOAD_STATES.LOADED
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
const type$6 = 'checklist';
|
|
1234
|
+
function Checklist(props) {
|
|
1235
|
+
const {
|
|
1236
|
+
disabled,
|
|
1237
|
+
errors = [],
|
|
1238
|
+
field,
|
|
1239
|
+
value = []
|
|
1240
|
+
} = props;
|
|
1241
|
+
const {
|
|
1242
|
+
description,
|
|
1243
|
+
id,
|
|
1244
|
+
label
|
|
1245
|
+
} = field;
|
|
1246
|
+
|
|
1247
|
+
const toggleCheckbox = v => {
|
|
1248
|
+
let newValue = [...value];
|
|
1249
|
+
|
|
1250
|
+
if (!newValue.includes(v)) {
|
|
1251
|
+
newValue.push(v);
|
|
1252
|
+
} else {
|
|
1253
|
+
newValue = newValue.filter(x => x != v);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
props.onChange({
|
|
1257
|
+
field,
|
|
1258
|
+
value: newValue
|
|
1259
|
+
});
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
const {
|
|
1263
|
+
state: loadState,
|
|
1264
|
+
values: options
|
|
1265
|
+
} = useValuesAsync(field);
|
|
1266
|
+
const {
|
|
1267
|
+
formId
|
|
1268
|
+
} = useContext(FormContext);
|
|
1269
|
+
return jsxs("div", {
|
|
1270
|
+
class: formFieldClasses(type$6, errors),
|
|
1271
|
+
children: [jsx(Label, {
|
|
1272
|
+
label: label
|
|
1273
|
+
}), loadState == LOAD_STATES.LOADED && options.map((v, index) => {
|
|
1274
|
+
return jsx(Label, {
|
|
1275
|
+
id: prefixId(`${id}-${index}`, formId),
|
|
1276
|
+
label: v.label,
|
|
1277
|
+
required: false,
|
|
1278
|
+
children: jsx("input", {
|
|
1279
|
+
checked: value.includes(v.value),
|
|
1280
|
+
class: "fjs-input",
|
|
1281
|
+
disabled: disabled,
|
|
1282
|
+
id: prefixId(`${id}-${index}`, formId),
|
|
1283
|
+
type: "checkbox",
|
|
1284
|
+
onClick: () => toggleCheckbox(v.value)
|
|
1285
|
+
})
|
|
1286
|
+
}, `${id}-${index}`);
|
|
1287
|
+
}), jsx(Description, {
|
|
1288
|
+
description: description
|
|
1289
|
+
}), jsx(Errors, {
|
|
1290
|
+
errors: errors
|
|
1291
|
+
})]
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
Checklist.create = function (options = {}) {
|
|
1296
|
+
if (options.valuesKey) return options;
|
|
1297
|
+
return {
|
|
1298
|
+
values: [{
|
|
1299
|
+
label: 'Value',
|
|
1300
|
+
value: 'value'
|
|
1301
|
+
}],
|
|
1302
|
+
...options
|
|
1303
|
+
};
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
Checklist.type = type$6;
|
|
1307
|
+
Checklist.label = 'Checklist';
|
|
1308
|
+
Checklist.keyed = true;
|
|
1309
|
+
Checklist.emptyValue = [];
|
|
1310
|
+
|
|
1157
1311
|
const noop$1 = () => false;
|
|
1158
1312
|
|
|
1159
1313
|
function FormField(props) {
|
|
@@ -1362,7 +1516,7 @@ function FormComponent(props) {
|
|
|
1362
1516
|
});
|
|
1363
1517
|
}
|
|
1364
1518
|
|
|
1365
|
-
const type$
|
|
1519
|
+
const type$5 = 'number';
|
|
1366
1520
|
function Number(props) {
|
|
1367
1521
|
const {
|
|
1368
1522
|
disabled,
|
|
@@ -1394,7 +1548,7 @@ function Number(props) {
|
|
|
1394
1548
|
formId
|
|
1395
1549
|
} = useContext(FormContext);
|
|
1396
1550
|
return jsxs("div", {
|
|
1397
|
-
class: formFieldClasses(type$
|
|
1551
|
+
class: formFieldClasses(type$5, errors),
|
|
1398
1552
|
children: [jsx(Label, {
|
|
1399
1553
|
id: prefixId(id, formId),
|
|
1400
1554
|
label: label,
|
|
@@ -1419,12 +1573,12 @@ Number.create = function (options = {}) {
|
|
|
1419
1573
|
};
|
|
1420
1574
|
};
|
|
1421
1575
|
|
|
1422
|
-
Number.type = type$
|
|
1576
|
+
Number.type = type$5;
|
|
1423
1577
|
Number.keyed = true;
|
|
1424
1578
|
Number.label = 'Number';
|
|
1425
1579
|
Number.emptyValue = null;
|
|
1426
1580
|
|
|
1427
|
-
const type$
|
|
1581
|
+
const type$4 = 'radio';
|
|
1428
1582
|
function Radio(props) {
|
|
1429
1583
|
const {
|
|
1430
1584
|
disabled,
|
|
@@ -1436,8 +1590,7 @@ function Radio(props) {
|
|
|
1436
1590
|
description,
|
|
1437
1591
|
id,
|
|
1438
1592
|
label,
|
|
1439
|
-
validate = {}
|
|
1440
|
-
values
|
|
1593
|
+
validate = {}
|
|
1441
1594
|
} = field;
|
|
1442
1595
|
const {
|
|
1443
1596
|
required
|
|
@@ -1450,26 +1603,30 @@ function Radio(props) {
|
|
|
1450
1603
|
});
|
|
1451
1604
|
};
|
|
1452
1605
|
|
|
1606
|
+
const {
|
|
1607
|
+
state: loadState,
|
|
1608
|
+
values: options
|
|
1609
|
+
} = useValuesAsync(field);
|
|
1453
1610
|
const {
|
|
1454
1611
|
formId
|
|
1455
1612
|
} = useContext(FormContext);
|
|
1456
1613
|
return jsxs("div", {
|
|
1457
|
-
class: formFieldClasses(type$
|
|
1614
|
+
class: formFieldClasses(type$4, errors),
|
|
1458
1615
|
children: [jsx(Label, {
|
|
1459
1616
|
label: label,
|
|
1460
1617
|
required: required
|
|
1461
|
-
}),
|
|
1618
|
+
}), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
|
|
1462
1619
|
return jsx(Label, {
|
|
1463
1620
|
id: prefixId(`${id}-${index}`, formId),
|
|
1464
|
-
label:
|
|
1621
|
+
label: option.label,
|
|
1465
1622
|
required: false,
|
|
1466
1623
|
children: jsx("input", {
|
|
1467
|
-
checked:
|
|
1624
|
+
checked: option.value === value,
|
|
1468
1625
|
class: "fjs-input",
|
|
1469
1626
|
disabled: disabled,
|
|
1470
1627
|
id: prefixId(`${id}-${index}`, formId),
|
|
1471
1628
|
type: "radio",
|
|
1472
|
-
onClick: () => onChange(
|
|
1629
|
+
onClick: () => onChange(option.value)
|
|
1473
1630
|
})
|
|
1474
1631
|
}, `${id}-${index}`);
|
|
1475
1632
|
}), jsx(Description, {
|
|
@@ -1481,6 +1638,7 @@ function Radio(props) {
|
|
|
1481
1638
|
}
|
|
1482
1639
|
|
|
1483
1640
|
Radio.create = function (options = {}) {
|
|
1641
|
+
if (options.valuesKey) return options;
|
|
1484
1642
|
return {
|
|
1485
1643
|
values: [{
|
|
1486
1644
|
label: 'Value',
|
|
@@ -1490,12 +1648,12 @@ Radio.create = function (options = {}) {
|
|
|
1490
1648
|
};
|
|
1491
1649
|
};
|
|
1492
1650
|
|
|
1493
|
-
Radio.type = type$
|
|
1651
|
+
Radio.type = type$4;
|
|
1494
1652
|
Radio.label = 'Radio';
|
|
1495
1653
|
Radio.keyed = true;
|
|
1496
1654
|
Radio.emptyValue = null;
|
|
1497
1655
|
|
|
1498
|
-
const type$
|
|
1656
|
+
const type$3 = 'select';
|
|
1499
1657
|
function Select(props) {
|
|
1500
1658
|
const {
|
|
1501
1659
|
disabled,
|
|
@@ -1507,8 +1665,7 @@ function Select(props) {
|
|
|
1507
1665
|
description,
|
|
1508
1666
|
id,
|
|
1509
1667
|
label,
|
|
1510
|
-
validate = {}
|
|
1511
|
-
values
|
|
1668
|
+
validate = {}
|
|
1512
1669
|
} = field;
|
|
1513
1670
|
const {
|
|
1514
1671
|
required
|
|
@@ -1523,11 +1680,15 @@ function Select(props) {
|
|
|
1523
1680
|
});
|
|
1524
1681
|
};
|
|
1525
1682
|
|
|
1683
|
+
const {
|
|
1684
|
+
state: loadState,
|
|
1685
|
+
values: options
|
|
1686
|
+
} = useValuesAsync(field);
|
|
1526
1687
|
const {
|
|
1527
1688
|
formId
|
|
1528
1689
|
} = useContext(FormContext);
|
|
1529
1690
|
return jsxs("div", {
|
|
1530
|
-
class: formFieldClasses(type$
|
|
1691
|
+
class: formFieldClasses(type$3, errors),
|
|
1531
1692
|
children: [jsx(Label, {
|
|
1532
1693
|
id: prefixId(id, formId),
|
|
1533
1694
|
label: label,
|
|
@@ -1540,10 +1701,10 @@ function Select(props) {
|
|
|
1540
1701
|
value: value || '',
|
|
1541
1702
|
children: [jsx("option", {
|
|
1542
1703
|
value: ""
|
|
1543
|
-
}),
|
|
1704
|
+
}), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
|
|
1544
1705
|
return jsx("option", {
|
|
1545
|
-
value:
|
|
1546
|
-
children:
|
|
1706
|
+
value: option.value,
|
|
1707
|
+
children: option.label
|
|
1547
1708
|
}, `${id}-${index}`);
|
|
1548
1709
|
})]
|
|
1549
1710
|
}), jsx(Description, {
|
|
@@ -1555,6 +1716,7 @@ function Select(props) {
|
|
|
1555
1716
|
}
|
|
1556
1717
|
|
|
1557
1718
|
Select.create = function (options = {}) {
|
|
1719
|
+
if (options.valuesKey) return options;
|
|
1558
1720
|
return {
|
|
1559
1721
|
values: [{
|
|
1560
1722
|
label: 'Value',
|
|
@@ -1564,11 +1726,313 @@ Select.create = function (options = {}) {
|
|
|
1564
1726
|
};
|
|
1565
1727
|
};
|
|
1566
1728
|
|
|
1567
|
-
Select.type = type$
|
|
1729
|
+
Select.type = type$3;
|
|
1568
1730
|
Select.label = 'Select';
|
|
1569
1731
|
Select.keyed = true;
|
|
1570
1732
|
Select.emptyValue = null;
|
|
1571
1733
|
|
|
1734
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
1735
|
+
var CloseIcon = (({
|
|
1736
|
+
styles = {},
|
|
1737
|
+
...props
|
|
1738
|
+
}) => /*#__PURE__*/React.createElement("svg", _extends({
|
|
1739
|
+
width: "16",
|
|
1740
|
+
height: "16",
|
|
1741
|
+
fill: "none",
|
|
1742
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
1743
|
+
}, props), /*#__PURE__*/React.createElement("path", {
|
|
1744
|
+
fillRule: "evenodd",
|
|
1745
|
+
clipRule: "evenodd",
|
|
1746
|
+
d: "M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7z",
|
|
1747
|
+
fill: "#000"
|
|
1748
|
+
})));
|
|
1749
|
+
|
|
1750
|
+
function useKeyDownAction(targetKey, action, listenerElement = window) {
|
|
1751
|
+
function downHandler({
|
|
1752
|
+
key
|
|
1753
|
+
}) {
|
|
1754
|
+
if (key === targetKey) {
|
|
1755
|
+
action();
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
useEffect(() => {
|
|
1760
|
+
listenerElement.addEventListener('keydown', downHandler);
|
|
1761
|
+
return () => {
|
|
1762
|
+
listenerElement.removeEventListener('keydown', downHandler);
|
|
1763
|
+
};
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
const DEFAULT_LABEL_GETTER = value => value;
|
|
1768
|
+
|
|
1769
|
+
const NOOP = () => {};
|
|
1770
|
+
|
|
1771
|
+
function DropdownList(props) {
|
|
1772
|
+
const {
|
|
1773
|
+
keyEventsListener = window,
|
|
1774
|
+
values = [],
|
|
1775
|
+
getLabel = DEFAULT_LABEL_GETTER,
|
|
1776
|
+
onValueSelected = NOOP,
|
|
1777
|
+
height = 235,
|
|
1778
|
+
emptyListMessage = 'No results'
|
|
1779
|
+
} = props;
|
|
1780
|
+
const [mouseControl, setMouseControl] = useState(true);
|
|
1781
|
+
const [focusedValueIndex, setFocusedValueIndex] = useState(0);
|
|
1782
|
+
const dropdownContainer = useRef();
|
|
1783
|
+
const mouseScreenPos = useRef();
|
|
1784
|
+
const focusedItem = useMemo(() => values.length ? values[focusedValueIndex] : null, [focusedValueIndex, values]);
|
|
1785
|
+
const changeFocusedValueIndex = useCallback(delta => {
|
|
1786
|
+
setFocusedValueIndex(x => Math.min(Math.max(0, x + delta), values.length - 1));
|
|
1787
|
+
}, [values.length]);
|
|
1788
|
+
useEffect(() => {
|
|
1789
|
+
if (focusedValueIndex === 0) return;
|
|
1790
|
+
|
|
1791
|
+
if (!focusedValueIndex || !values.length) {
|
|
1792
|
+
setFocusedValueIndex(0);
|
|
1793
|
+
} else if (focusedValueIndex >= values.length) {
|
|
1794
|
+
setFocusedValueIndex(values.length - 1);
|
|
1795
|
+
}
|
|
1796
|
+
}, [focusedValueIndex, values.length]);
|
|
1797
|
+
useKeyDownAction('ArrowUp', () => {
|
|
1798
|
+
if (values.length) {
|
|
1799
|
+
changeFocusedValueIndex(-1);
|
|
1800
|
+
setMouseControl(false);
|
|
1801
|
+
}
|
|
1802
|
+
}, keyEventsListener);
|
|
1803
|
+
useKeyDownAction('ArrowDown', () => {
|
|
1804
|
+
if (values.length) {
|
|
1805
|
+
changeFocusedValueIndex(1);
|
|
1806
|
+
setMouseControl(false);
|
|
1807
|
+
}
|
|
1808
|
+
}, keyEventsListener);
|
|
1809
|
+
useKeyDownAction('Enter', () => {
|
|
1810
|
+
if (focusedItem) {
|
|
1811
|
+
onValueSelected(focusedItem);
|
|
1812
|
+
}
|
|
1813
|
+
}, keyEventsListener);
|
|
1814
|
+
useEffect(() => {
|
|
1815
|
+
const individualEntries = dropdownContainer.current.children;
|
|
1816
|
+
|
|
1817
|
+
if (individualEntries.length && !mouseControl) {
|
|
1818
|
+
individualEntries[focusedValueIndex].scrollIntoView({
|
|
1819
|
+
block: 'nearest',
|
|
1820
|
+
inline: 'nearest'
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
}, [focusedValueIndex, mouseControl]);
|
|
1824
|
+
|
|
1825
|
+
const mouseMove = (e, i) => {
|
|
1826
|
+
const userMoved = !mouseScreenPos.current || mouseScreenPos.current.x !== e.screenX && mouseScreenPos.current.y !== e.screenY;
|
|
1827
|
+
|
|
1828
|
+
if (userMoved) {
|
|
1829
|
+
mouseScreenPos.current = {
|
|
1830
|
+
x: e.screenX,
|
|
1831
|
+
y: e.screenY
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
if (!mouseControl) {
|
|
1835
|
+
setMouseControl(true);
|
|
1836
|
+
setFocusedValueIndex(i);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1841
|
+
return jsxs("div", {
|
|
1842
|
+
ref: dropdownContainer,
|
|
1843
|
+
tabIndex: -1,
|
|
1844
|
+
class: "fjs-dropdownlist",
|
|
1845
|
+
style: {
|
|
1846
|
+
maxHeight: height
|
|
1847
|
+
},
|
|
1848
|
+
children: [!!values.length && values.map((v, i) => {
|
|
1849
|
+
return jsx("div", {
|
|
1850
|
+
class: 'fjs-dropdownlist-item' + (focusedValueIndex === i ? ' focused' : ''),
|
|
1851
|
+
onMouseMove: e => mouseMove(e, i),
|
|
1852
|
+
onMouseEnter: mouseControl ? () => setFocusedValueIndex(i) : undefined,
|
|
1853
|
+
onMouseDown: e => {
|
|
1854
|
+
e.preventDefault();
|
|
1855
|
+
onValueSelected(v);
|
|
1856
|
+
},
|
|
1857
|
+
children: getLabel(v)
|
|
1858
|
+
});
|
|
1859
|
+
}), !values.length && jsx("div", {
|
|
1860
|
+
class: "fjs-dropdownlist-empty",
|
|
1861
|
+
children: emptyListMessage
|
|
1862
|
+
})]
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
const type$2 = 'taglist';
|
|
1867
|
+
function Taglist(props) {
|
|
1868
|
+
const {
|
|
1869
|
+
disabled,
|
|
1870
|
+
errors = [],
|
|
1871
|
+
field,
|
|
1872
|
+
value: values = []
|
|
1873
|
+
} = props;
|
|
1874
|
+
const {
|
|
1875
|
+
description,
|
|
1876
|
+
id,
|
|
1877
|
+
label
|
|
1878
|
+
} = field;
|
|
1879
|
+
const {
|
|
1880
|
+
formId
|
|
1881
|
+
} = useContext(FormContext);
|
|
1882
|
+
const [filter, setFilter] = useState('');
|
|
1883
|
+
const [selectedValues, setSelectedValues] = useState([]);
|
|
1884
|
+
const [filteredValues, setFilteredValues] = useState([]);
|
|
1885
|
+
const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
|
|
1886
|
+
const [hasValuesLeft, setHasValuesLeft] = useState(true);
|
|
1887
|
+
const [isEscapeClosed, setIsEscapeClose] = useState(false);
|
|
1888
|
+
const searchbarRef = useRef();
|
|
1889
|
+
const {
|
|
1890
|
+
state: loadState,
|
|
1891
|
+
values: options
|
|
1892
|
+
} = useValuesAsync(field); // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
|
|
1893
|
+
|
|
1894
|
+
useEffect(() => {
|
|
1895
|
+
if (loadState === LOAD_STATES.LOADED) {
|
|
1896
|
+
const selectedValues = values.map(v => options.find(o => o.value === v)).filter(v => v !== undefined);
|
|
1897
|
+
setSelectedValues(selectedValues);
|
|
1898
|
+
} else {
|
|
1899
|
+
setSelectedValues([]);
|
|
1900
|
+
}
|
|
1901
|
+
}, [JSON.stringify(values), options, loadState]);
|
|
1902
|
+
useEffect(() => {
|
|
1903
|
+
if (loadState === LOAD_STATES.LOADED) {
|
|
1904
|
+
setFilteredValues(options.filter(o => o.label && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value)));
|
|
1905
|
+
} else {
|
|
1906
|
+
setFilteredValues([]);
|
|
1907
|
+
}
|
|
1908
|
+
}, [filter, JSON.stringify(values), options]);
|
|
1909
|
+
useEffect(() => {
|
|
1910
|
+
setHasValuesLeft(selectedValues.length < options.length);
|
|
1911
|
+
}, [selectedValues.length, options.length]);
|
|
1912
|
+
|
|
1913
|
+
const onFilterChange = ({
|
|
1914
|
+
target
|
|
1915
|
+
}) => {
|
|
1916
|
+
setIsEscapeClose(false);
|
|
1917
|
+
setFilter(target.value);
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
const selectValue = option => {
|
|
1921
|
+
setFilter('');
|
|
1922
|
+
props.onChange({
|
|
1923
|
+
value: [...values, option.value],
|
|
1924
|
+
field
|
|
1925
|
+
});
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
const deselectValue = option => {
|
|
1929
|
+
props.onChange({
|
|
1930
|
+
value: values.filter(v => v != option.value),
|
|
1931
|
+
field
|
|
1932
|
+
});
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
const onInputKeyDown = e => {
|
|
1936
|
+
switch (e.key) {
|
|
1937
|
+
case 'ArrowUp':
|
|
1938
|
+
case 'ArrowDown':
|
|
1939
|
+
// We do not want the cursor to seek in the search field when we press up and down
|
|
1940
|
+
e.preventDefault();
|
|
1941
|
+
break;
|
|
1942
|
+
|
|
1943
|
+
case 'Backspace':
|
|
1944
|
+
if (!filter && selectedValues.length) {
|
|
1945
|
+
deselectValue(selectedValues[selectedValues.length - 1]);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
break;
|
|
1949
|
+
|
|
1950
|
+
case 'Escape':
|
|
1951
|
+
setIsEscapeClose(true);
|
|
1952
|
+
break;
|
|
1953
|
+
|
|
1954
|
+
case 'Enter':
|
|
1955
|
+
if (isEscapeClosed) {
|
|
1956
|
+
setIsEscapeClose(false);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
return jsxs("div", {
|
|
1964
|
+
class: formFieldClasses(type$2, errors),
|
|
1965
|
+
children: [jsx(Label, {
|
|
1966
|
+
label: label,
|
|
1967
|
+
id: prefixId(id, formId)
|
|
1968
|
+
}), jsxs("div", {
|
|
1969
|
+
class: classNames('fjs-taglist', {
|
|
1970
|
+
'disabled': disabled
|
|
1971
|
+
}),
|
|
1972
|
+
children: [!disabled && loadState === LOAD_STATES.LOADED && selectedValues.map(sv => {
|
|
1973
|
+
return jsxs("div", {
|
|
1974
|
+
class: "fjs-taglist-tag",
|
|
1975
|
+
onMouseDown: e => e.preventDefault(),
|
|
1976
|
+
children: [jsx("span", {
|
|
1977
|
+
class: "fjs-taglist-tag-label",
|
|
1978
|
+
children: sv.label
|
|
1979
|
+
}), jsx("span", {
|
|
1980
|
+
class: "fjs-taglist-tag-remove",
|
|
1981
|
+
onMouseDown: () => deselectValue(sv),
|
|
1982
|
+
children: jsx(CloseIcon, {})
|
|
1983
|
+
})]
|
|
1984
|
+
});
|
|
1985
|
+
}), jsx("input", {
|
|
1986
|
+
disabled: disabled,
|
|
1987
|
+
class: "fjs-taglist-input",
|
|
1988
|
+
ref: searchbarRef,
|
|
1989
|
+
id: prefixId(`${id}-search`, formId),
|
|
1990
|
+
onChange: onFilterChange,
|
|
1991
|
+
type: "text",
|
|
1992
|
+
value: filter,
|
|
1993
|
+
placeholder: 'Search',
|
|
1994
|
+
autoComplete: "off",
|
|
1995
|
+
onKeyDown: e => onInputKeyDown(e),
|
|
1996
|
+
onMouseDown: () => setIsEscapeClose(false),
|
|
1997
|
+
onFocus: () => setIsDropdownExpanded(true),
|
|
1998
|
+
onBlur: () => {
|
|
1999
|
+
setIsDropdownExpanded(false);
|
|
2000
|
+
setFilter('');
|
|
2001
|
+
}
|
|
2002
|
+
})]
|
|
2003
|
+
}), jsx("div", {
|
|
2004
|
+
class: "fjs-taglist-anchor",
|
|
2005
|
+
children: !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed && jsx(DropdownList, {
|
|
2006
|
+
values: filteredValues,
|
|
2007
|
+
getLabel: v => v.label,
|
|
2008
|
+
onValueSelected: v => selectValue(v),
|
|
2009
|
+
emptyListMessage: hasValuesLeft ? 'No results' : 'All values selected',
|
|
2010
|
+
listenerElement: searchbarRef.current
|
|
2011
|
+
})
|
|
2012
|
+
}), jsx(Description, {
|
|
2013
|
+
description: description
|
|
2014
|
+
}), jsx(Errors, {
|
|
2015
|
+
errors: errors
|
|
2016
|
+
})]
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
Taglist.create = function (options = {}) {
|
|
2021
|
+
if (options.valuesKey) return options;
|
|
2022
|
+
return {
|
|
2023
|
+
values: [{
|
|
2024
|
+
label: 'Value',
|
|
2025
|
+
value: 'value'
|
|
2026
|
+
}],
|
|
2027
|
+
...options
|
|
2028
|
+
};
|
|
2029
|
+
};
|
|
2030
|
+
|
|
2031
|
+
Taglist.type = type$2;
|
|
2032
|
+
Taglist.label = 'Taglist';
|
|
2033
|
+
Taglist.keyed = true;
|
|
2034
|
+
Taglist.emptyValue = [];
|
|
2035
|
+
|
|
1572
2036
|
const type$1 = 'text';
|
|
1573
2037
|
function Text(props) {
|
|
1574
2038
|
const {
|
|
@@ -1657,7 +2121,7 @@ Textfield.label = 'Text Field';
|
|
|
1657
2121
|
Textfield.keyed = true;
|
|
1658
2122
|
Textfield.emptyValue = '';
|
|
1659
2123
|
|
|
1660
|
-
const formFields = [Button, Checkbox, Default, Number, Radio, Select, Text, Textfield];
|
|
2124
|
+
const formFields = [Button, Checkbox, Checklist, Default, Number, Radio, Select, Taglist, Text, Textfield];
|
|
1661
2125
|
|
|
1662
2126
|
class FormFields {
|
|
1663
2127
|
constructor() {
|
|
@@ -2144,7 +2608,7 @@ class Form {
|
|
|
2144
2608
|
|
|
2145
2609
|
}
|
|
2146
2610
|
|
|
2147
|
-
const schemaVersion =
|
|
2611
|
+
const schemaVersion = 5;
|
|
2148
2612
|
/**
|
|
2149
2613
|
* @typedef { import('./types').CreateFormOptions } CreateFormOptions
|
|
2150
2614
|
*/
|
|
@@ -2169,5 +2633,5 @@ function createForm(options) {
|
|
|
2169
2633
|
});
|
|
2170
2634
|
}
|
|
2171
2635
|
|
|
2172
|
-
export { Button, Checkbox, Default, Form, FormComponent, FormContext, FormFieldRegistry, FormFields, FormRenderContext, Number, Radio, Select, Text, Textfield, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
|
|
2636
|
+
export { Button, Checkbox, Checklist, Default, Form, FormComponent, FormContext, FormFieldRegistry, FormFields, FormRenderContext, Number, Radio, Select, Taglist, Text, Textfield, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
|
|
2173
2637
|
//# sourceMappingURL=index.es.js.map
|