@luomus/laji-form 15.1.71 → 15.1.73

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/styles.css CHANGED
@@ -2899,25 +2899,6 @@ body .laji-form {
2899
2899
  .laji-form .hidden {
2900
2900
  display: none;
2901
2901
  }
2902
- .laji-form .checkbox-container {
2903
- display: table;
2904
- }
2905
-
2906
- .laji-form-checkbox-selected-value-glyph, .laji-form-checkbox-unselected-value-glyph {
2907
- pointer-events: none;
2908
- }
2909
- .laji-form-checkbox-selected-value-glyph:before, .laji-form-checkbox-unselected-value-glyph:before {
2910
- content: "•";
2911
- margin-right: 6px;
2912
- height: 0;
2913
- font-weight: 900;
2914
- color: #9e9e9e;
2915
- font-size: 18px;
2916
- vertical-align: -2px;
2917
- }
2918
- .laji-form-checkbox-unselected-value-glyph:before {
2919
- visibility: hidden;
2920
- }
2921
2902
 
2922
2903
  .laji-form.map-dialog .modal-content, .laji-form.map-dialog .modal-body {
2923
2904
  width: auto;
@@ -3931,3 +3912,28 @@ body .laji-form {
3931
3912
  margin-left: -20px !important;
3932
3913
  }
3933
3914
 
3915
+ .laji-form .checkbox-container label {
3916
+ font-weight: initial;
3917
+ margin-right: 5px;
3918
+ }
3919
+ .laji-form input[type="radio"] {
3920
+ margin-right: 5px;
3921
+ margin-top: -2px;
3922
+ vertical-align: middle;
3923
+ accent-color: #7d7d7d;
3924
+ }
3925
+ .laji-form .has-error input[type="radio"] {
3926
+ accent-color: #a94442 !important;
3927
+ }
3928
+
3929
+ .laji-form .checkbox-container label input:focus {
3930
+ outline-offset: 0;
3931
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 5px 2px rgb(0 142 255);
3932
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 5px 2px rgb(0 142 255);
3933
+ }
3934
+ .laji-form .checkbox-container label input:focus-within {
3935
+ outline-offset: 0;
3936
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 5px 2px rgb(0 142 255);
3937
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 5px 2px rgb(0 142 255);
3938
+ }
3939
+
@@ -5,7 +5,7 @@ import { ReactUtilsType } from "../utils";
5
5
  import { Theme } from "../themes/theme";
6
6
  import { ContextProps } from "../ReactContext";
7
7
  import Form from "@rjsf/core";
8
- import { Field, Widget, TemplatesType } from "@rjsf/utils";
8
+ import { Field, Widget, TemplatesType, ErrorSchema } from "@rjsf/utils";
9
9
  import ApiClient, { ApiClientImplementation } from "../ApiClient";
10
10
  import KeyHandlerService, { ShortcutKeys } from "../services/key-handler-service";
11
11
  import SettingsService, { Settings } from "../services/settings-service";
@@ -53,19 +53,21 @@ export interface LajiFormProps extends HasMaybeChildren {
53
53
  onSubmit?: (data: {
54
54
  formData: any;
55
55
  }) => void;
56
- onValidationError?: (extraErrors: any) => void;
56
+ onValidationError?: (extraErrors: ErrorSchema) => void;
57
57
  validators?: any;
58
58
  warnings?: any;
59
59
  onSettingsChange?: (settings: any, global: boolean) => void;
60
60
  mediaMetadata?: MediaMetadata;
61
61
  theme?: Theme;
62
62
  lajiGeoServerAddress?: string;
63
+ extraErrors: ErrorSchema;
63
64
  }
64
65
  export interface LajiFormState {
65
66
  submitHooks?: SubmitHook[];
66
67
  formContext: FormContext;
67
68
  formData?: any;
68
- extraErrors?: any;
69
+ extraErrors?: ErrorSchema;
70
+ externalErrors?: ErrorSchema;
69
71
  error?: boolean;
70
72
  runningSubmitHooks?: boolean;
71
73
  }
@@ -282,7 +282,8 @@ class LajiForm extends React.Component {
282
282
  this.validateAndSubmit = (warnings = true, onlySchema = false) => {
283
283
  const { formData } = this.state;
284
284
  const { onValidationError, onSubmit } = this.props;
285
- return this.validate(warnings, true, onlySchema).then((valid) => {
285
+ this.setState({ externalErrors: undefined });
286
+ return this.validate(warnings, true, onlySchema).then(valid => {
286
287
  if (formData !== this.state.formData) {
287
288
  this.validateAndSubmit(warnings, onlySchema);
288
289
  }
@@ -329,12 +330,15 @@ class LajiForm extends React.Component {
329
330
  this.cachedNonliveValidations = (0, deepmerge_1.default)(schemaErrors, _validations);
330
331
  block && this.memoizedFormContext.services.blocker.pop();
331
332
  }
332
- const merged = (0, deepmerge_1.default)(_liveValidations, !nonlive
333
+ const mergedInternalErrors = (0, deepmerge_1.default)(_liveValidations, !nonlive
333
334
  ? (this.cachedNonliveValidations || {})
334
335
  : (0, deepmerge_1.default)(_validations, schemaErrors));
336
+ const mergedAll = (0, deepmerge_1.default)(this.state.externalErrors || {}, mergedInternalErrors);
335
337
  this.validating = false;
336
- resolve(!Object.keys(merged).length);
337
- !equals((this.state.extraErrors || {}), merged) && this.setState({ extraErrors: merged }, this.popErrorListIfNeeded);
338
+ resolve(!Object.keys(mergedAll).length);
339
+ if (!equals((this.state.extraErrors || {}), mergedAll)) {
340
+ this.setState({ extraErrors: mergedAll }, this.popErrorListIfNeeded);
341
+ }
338
342
  }).catch((e) => {
339
343
  block && this.memoizedFormContext.services.blocker.pop();
340
344
  throw e;
@@ -516,11 +520,16 @@ class LajiForm extends React.Component {
516
520
  if (this.apiClient && "lang" in props && this.props.lang !== props.lang) {
517
521
  this.apiClient.setLang(props.lang);
518
522
  }
519
- this.setState(this.getStateFromProps(props));
523
+ this.setState(this.getStateFromProps(props), this.popErrorListIfNeeded);
520
524
  }
521
525
  getStateFromProps(props) {
526
+ var _a;
522
527
  const state = {
523
- formContext: this.getMemoizedFormContext(props)
528
+ formContext: this.getMemoizedFormContext(props),
529
+ externalErrors: props.extraErrors,
530
+ extraErrors: ((_a = this.state) === null || _a === void 0 ? void 0 : _a.extraErrors)
531
+ ? (0, deepmerge_1.default)(this.state.extraErrors, props.extraErrors || {})
532
+ : props.extraErrors
524
533
  };
525
534
  if (((!this.state && props.schema && Object.keys(props.schema).length) || (this.state && !("formData" in this.state))) || ("formData" in props && props.formData !== this.props.formData)) {
526
535
  state.formData = state.formContext.services.ids.addLajiFormIds((0, utils_1.getDefaultFormState)(props.schema, props.formData, props.schema))[0];
@@ -632,7 +641,7 @@ class LajiForm extends React.Component {
632
641
  showShortcutsButton && this.props.showShortcutButton !== false && shortcuts && (React.createElement(components_1.TooltipComponent, { tooltip: this.getShorcutButtonTooltip() },
633
642
  React.createElement(components_1.Button, { variant: undefined, onClick: this.toggleHelp }, translations.Shortcuts))),
634
643
  React.createElement(components_1.FailedBackgroundJobsPanel, { jobs: this.state.submitHooks, schema: this.props.schema, uiSchema: uiSchema, formContext: this.state.formContext, errorClickHandler: this.memoizedFormContext.services.focus.focus, ref: this.bgJobRef }),
635
- React.createElement(core_1.default, Object.assign({}, this.props, { formData: this.state.formData, uiSchema: uiSchema, ref: this.formRef, onChange: this.onChange, onSubmit: this.onSubmit, fields: this.getFields(this.props.fields), widgets: this.getWidgets(this.props.widgets), templates: this.getTemplates(this.props.templates), formContext: this.state.formContext, validator: validator_ajv6_1.default, noValidate: true, extraErrors: this.state.extraErrors, noHtml5Validate: true, liveValidate: true, autoComplete: "off", tagName: "div", readonly: readonly, disabled: disabled }), this.props.children),
644
+ React.createElement(core_1.default, Object.assign({}, this.props, { formData: this.state.formData, uiSchema: uiSchema, ref: this.formRef, onChange: this.onChange, onSubmit: this.onSubmit, fields: this.getFields(this.props.fields), widgets: this.getWidgets(this.props.widgets), templates: this.getTemplates(this.props.templates), formContext: this.state.formContext, validator: validator_ajv6_1.default, noValidate: true, extraErrors: this.state.extraErrors || {}, noHtml5Validate: true, liveValidate: true, autoComplete: "off", tagName: "div", readonly: readonly, disabled: disabled }), this.props.children),
636
645
  (!this.props.children && this.props.renderSubmit !== false) ? (React.createElement(components_1.Button, { id: "submit", onClick: this.submit, disabled: readonly || disabled }, this.props.submitText || translations.Submit)) : null,
637
646
  shortcuts &&
638
647
  React.createElement(Panel, { ref: this.shortcutHelpRef, className: "shortcut-help laji-form-popped z-depth-3 hidden", style: { top: (this.props.topOffset || 0) + 5, bottom: (this.props.bottomOffset || 0) + 5 }, variant: "info" },
@@ -17,24 +17,14 @@ export default class CheckboxWidget extends React.Component<any, any, any> {
17
17
  value: PropTypes.Requireable<boolean>;
18
18
  };
19
19
  constructor(props: any);
20
- trueRef: React.RefObject<any>;
21
- falseRef: React.RefObject<any>;
22
- undefinedRef: React.RefObject<any>;
23
- groupRef: React.RefObject<any>;
24
- componentDidMount(): void;
25
- componentDidUpdate(): void;
26
- rmInputTabIndices: () => void;
20
+ containerRef: React.RefObject<any>;
21
+ onKeyDown: (e: any) => void;
27
22
  onChange: (value: any) => void;
28
- onButtonGroupChange: (value: any) => void;
23
+ onChangeTrue: () => void;
24
+ onChangeFalse: () => void;
25
+ onChangeUndefined: () => void;
29
26
  getOptions: (props: any) => any;
30
27
  render(): JSX.Element;
31
- getToggleMode: (props: any) => boolean;
32
- onGroupKeyDown: any;
33
- onTrueKeyDown: any;
34
- onFalseKeyDown: any;
35
- onUndefinedKeyDown: any;
36
- toggle: (e: any) => void;
37
- formatValue(value: any, options: any, props: any): any;
38
28
  }
39
29
  import * as React from "react";
40
30
  import * as PropTypes from "prop-types";
@@ -37,133 +37,67 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const React = __importStar(require("react"));
40
- const react_dom_1 = require("react-dom");
41
40
  const PropTypes = __importStar(require("prop-types"));
42
41
  const utils_1 = require("../../utils");
43
42
  const ReactContext_1 = __importDefault(require("../../ReactContext"));
44
43
  class CheckboxWidget extends React.Component {
45
44
  constructor(props) {
46
45
  super(props);
47
- this.rmInputTabIndices = () => {
48
- [this.trueRef, this.falseRef, this.undefinedRef].forEach(node => {
49
- const domNode = (0, react_dom_1.findDOMNode)(node.current);
50
- if (!domNode) {
51
- return;
52
- }
53
- const input = domNode.getElementsByTagName("input")[0];
54
- if (!input) {
55
- return;
56
- }
57
- input.setAttribute("tabindex", -1);
58
- });
46
+ this.containerRef = React.createRef();
47
+ this.onKeyDown = (e) => {
48
+ if (!e.key.startsWith("Arrow")) {
49
+ return;
50
+ }
51
+ const inputs = this.containerRef.current.querySelectorAll("input");
52
+ console.log(inputs, document.activeElement);
53
+ if (["ArrowRight", "ArrowDown"].includes(e.key) && document.activeElement === inputs[inputs.length - 1]) {
54
+ e.preventDefault();
55
+ }
56
+ else if (["ArrowLeft", "ArrowUp"].includes(e.key) && document.activeElement === inputs[0]) {
57
+ e.preventDefault();
58
+ }
59
59
  };
60
60
  this.onChange = (value) => {
61
61
  if (value !== undefined && this.getOptions(this.props).invert) {
62
62
  value = !value;
63
63
  }
64
+ console.log("change", value);
64
65
  this.props.onChange(value);
65
66
  };
66
- this.onButtonGroupChange = (value) => {
67
- if (value === "undefined") {
68
- value = undefined;
69
- }
70
- this.onChange(value);
71
- };
67
+ this.onChangeTrue = () => this.onChange(true);
68
+ this.onChangeFalse = () => this.onChange(false);
69
+ this.onChangeUndefined = () => this.onChange(undefined);
72
70
  this.getOptions = (props) => {
73
71
  const { Yes, No, Unknown } = props.registry.formContext.translations;
74
72
  return Object.assign({ allowUndefined: true, showUndefined: true, invert: false, trueLabel: Yes, falseLabel: No, unknownLabel: Unknown, required: this.props.required }, (0, utils_1.getUiOptions)(props));
75
73
  };
76
- this.getToggleMode = (props) => {
77
- const { allowUndefined, showUndefined } = this.getOptions(props);
78
- const displayUndefined = (allowUndefined && showUndefined);
79
- return !displayUndefined;
80
- };
81
- this.onGroupKeyDown = this.props.formContext.utils.keyboardClick((e) => {
82
- this.getToggleMode(this.props) && this.toggle(e);
83
- });
84
- this.onTrueKeyDown = this.props.formContext.utils.keyboardClick((e) => {
85
- this.onChange(true);
86
- e.preventDefault();
87
- e.stopPropagation();
88
- });
89
- this.onFalseKeyDown = this.props.formContext.utils.keyboardClick((e) => {
90
- this.onChange(false);
91
- e.preventDefault();
92
- e.stopPropagation();
93
- });
94
- this.onUndefinedKeyDown = this.props.formContext.utils.keyboardClick((e) => {
95
- this.onChange(undefined);
96
- e.preventDefault();
97
- e.stopPropagation();
98
- });
99
- this.toggle = (e) => {
100
- if (this.props.disabled || this.props.readonly) {
101
- return;
102
- }
103
- const nodes = [this.trueRef, this.falseRef, this.groupRef].map(r => (0, react_dom_1.findDOMNode)(r.current));
104
- if (!nodes.includes(e.target)) {
105
- return;
106
- }
107
- e.preventDefault();
108
- this.props.onChange(!this.props.value);
109
- };
110
- this.trueRef = React.createRef();
111
- this.falseRef = React.createRef();
112
- this.undefinedRef = React.createRef();
113
- this.groupRef = React.createRef();
114
- }
115
- componentDidMount() {
116
- this.rmInputTabIndices();
117
- }
118
- componentDidUpdate() {
119
- this.rmInputTabIndices();
120
74
  }
121
75
  render() {
122
- const { value, disabled, readonly, label, id } = this.props;
76
+ const { value, disabled, readonly, label } = this.props;
123
77
  const options = this.getOptions(this.props);
124
78
  const { allowUndefined, showUndefined, invert, trueLabel, falseLabel, unknownLabel, required, label: uiOptionsLabel } = options;
125
79
  const hasLabel = !(0, utils_1.isEmptyString)(label) && uiOptionsLabel !== false;
126
- // "undefined" for silencing ToggleButton warning.
127
- const _value = value === undefined
128
- ? "undefined"
129
- : invert
130
- ? !value
131
- : value;
132
- const { ButtonToolbar, ToggleButton, ToggleButtonGroup } = this.context.theme;
80
+ const _value = invert
81
+ ? !value
82
+ : value;
133
83
  const displayUndefined = (allowUndefined && showUndefined);
134
- const toggleMode = this.getToggleMode(this.props);
135
84
  const _disabled = disabled || readonly;
136
- const commonProps = {
137
- disabled: _disabled,
138
- tabIndex: (toggleMode || _disabled) ? undefined : 0
139
- };
140
85
  const tabTargetClass = "laji-form-checkbox-widget-tab-target";
141
- const selectedValueGlyph = React.createElement("span", { className: "laji-form-checkbox-selected-value-glyph" });
142
- const unselectedValueGlyph = React.createElement("span", { className: "laji-form-checkbox-unselected-value-glyph" });
143
- const checkbox = (React.createElement(ButtonToolbar, { className: (0, utils_1.classNames)("laji-form-checkbox-buttons", toggleMode && "desktop-layout") },
144
- React.createElement(ToggleButtonGroup, Object.assign({ ref: this.groupRef, type: "radio", value: [_value], name: this.props.id, onChange: this.onButtonGroupChange, onKeyDown: this.onGroupKeyDown, className: (0, utils_1.classNames)(toggleMode && tabTargetClass) }, commonProps, { tabIndex: (toggleMode && !_disabled) ? 0 : undefined }),
145
- React.createElement(ToggleButton, Object.assign({ id: `${id}-true`, ref: this.trueRef, value: true, onClick: toggleMode ? this.toggle : undefined, className: (0, utils_1.classNames)(_value === true && tabTargetClass), onKeyDown: this.onTrueKeyDown }, commonProps),
146
- _value === true ? selectedValueGlyph : unselectedValueGlyph,
147
- trueLabel),
148
- React.createElement(ToggleButton, Object.assign({ id: `${id}-false`, ref: this.falseRef, value: false, onClick: toggleMode ? this.toggle : undefined, className: (0, utils_1.classNames)(_value === false && tabTargetClass), onKeyDown: this.onFalseKeyDown }, commonProps),
149
- _value === false ? selectedValueGlyph : unselectedValueGlyph,
150
- falseLabel),
151
- (displayUndefined ?
152
- React.createElement(ToggleButton, Object.assign({ id: `${id}-undefined`, ref: this.undefinedRef, value: "undefined", className: (0, utils_1.classNames)(value === undefined && tabTargetClass) }, commonProps, { onKeyDown: this.onUndefinedKeyDown }),
153
- _value === "undefined" ? selectedValueGlyph : unselectedValueGlyph,
154
- unknownLabel) : null))));
86
+ const checkbox = (React.createElement("div", { ref: this.containerRef, className: "checkbox-container", disabled: _disabled },
87
+ React.createElement("label", null,
88
+ React.createElement("input", { type: "radio", value: "true", checked: _value === true, onChange: this.onChangeTrue, className: tabTargetClass, onKeyDown: this.onKeyDown }),
89
+ trueLabel),
90
+ React.createElement("label", null,
91
+ React.createElement("input", { type: "radio", value: "false", checked: _value === false, onChange: this.onChangeFalse, onKeyDown: this.onKeyDown }),
92
+ falseLabel),
93
+ displayUndefined && (React.createElement("label", null,
94
+ React.createElement("input", { type: "radio", value: "undefined", checked: _value === undefined, onChange: this.onChangeUndefined, onKeyDown: this.onKeyDown }),
95
+ unknownLabel))));
155
96
  const { Label } = this.props.formContext;
156
97
  return !hasLabel ? checkbox : React.createElement(React.Fragment, null,
157
98
  React.createElement(Label, { label: label, required: required, uiSchema: this.props.uiSchema, registry: this.props.registry, id: this.props.id }),
158
99
  checkbox);
159
100
  }
160
- formatValue(value, options, props) {
161
- return value === undefined
162
- ? ""
163
- : value === true
164
- ? props.formContext.translations.Yes
165
- : props.formContext.translations.No;
166
- }
167
101
  }
168
102
  CheckboxWidget.contextType = ReactContext_1.default;
169
103
  CheckboxWidget.propTypes = {
package/lib/themes/bs3.js CHANGED
@@ -164,6 +164,7 @@ const theme = {
164
164
  ControlLabel: ControlLabel_1.default,
165
165
  Checkbox: Checkbox_1.default,
166
166
  ToggleButton: ToggleButton_1.default,
167
- ToggleButtonGroup: ToggleButtonGroup_1.default
167
+ ToggleButtonGroup: ToggleButtonGroup_1.default,
168
+ // Radio
168
169
  };
169
170
  exports.default = theme;
package/lib/utils.js CHANGED
@@ -292,16 +292,18 @@ function isMultiSelect(schema, uiSchema) {
292
292
  function isSelect(schema) {
293
293
  return (0, utils_1.isSelect)(validator_ajv6_1.default, schema);
294
294
  }
295
- const SWITCH_CLASS = "laji-form-checkbox-widget-tab-target";
295
+ const CHECKBOX_CLASS = "laji-form-checkbox-widget-tab-target";
296
296
  const IMG_ADD_CLASS = "laji-form-drop-zone";
297
297
  const tabbableSelectors = [
298
298
  "input",
299
299
  "select",
300
- "textarea",
301
- `.${SWITCH_CLASS}`,
300
+ "textarea"
301
+ ].map(type => `${type}:not([type="hidden"]):not(:disabled):not([readonly]):not([type="file"]):not(.leaflet-control-layers-selector):not(.laji-map-input):not([type="radio"])`);
302
+ const tabbableSelectorsQuery = [
303
+ ...tabbableSelectors,
304
+ `.${CHECKBOX_CLASS}`,
302
305
  `.${IMG_ADD_CLASS}`
303
- ].map(type => `${type}:not([type="hidden"]):not(:disabled):not([readonly]):not([type="file"]):not(.leaflet-control-layers-selector):not(.laji-map-input)`);
304
- const tabbableSelectorsQuery = tabbableSelectors.join(", ");
306
+ ].join(", ");
305
307
  function getTabbableFields(elem, reverse) {
306
308
  const fieldsNodeList = elem.querySelectorAll(tabbableSelectorsQuery);
307
309
  let fields = Array.from(fieldsNodeList).filter(node => node.tabIndex !== -1);
@@ -312,7 +314,7 @@ function getTabbableFields(elem, reverse) {
312
314
  function isTabbableInput(elem) {
313
315
  return elem.id.match(/^_laji-form_/)
314
316
  || ["input", "select", "textarea"].includes(elem.tagName.toLowerCase())
315
- || elem.className.includes(SWITCH_CLASS)
317
+ || elem.className.includes(CHECKBOX_CLASS)
316
318
  || elem.className.includes(IMG_ADD_CLASS);
317
319
  }
318
320
  function canFocusNextInput(root, inputElem) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luomus/laji-form",
3
- "version": "15.1.71",
3
+ "version": "15.1.73",
4
4
  "description": "React module capable of building dynamic forms from Laji form json schemas",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -228,14 +228,14 @@ class Form {
228
228
  });
229
229
  }
230
230
  getBooleanWidget(str) {
231
- const $container = this.$locate(str).locator(".btn-toolbar");
231
+ const $container = this.$locate(str).locator(".checkbox-container");
232
232
  return {
233
233
  $container,
234
- $true: $container.locator(".btn").nth(0), // eslint-disable-line protractor/use-first-last
235
- $false: $container.locator(".btn").nth(1),
236
- $undefined: this.$locate(str).locator(".btn").nth(2),
237
- $active: $container.locator(".btn.active"),
238
- $nonactive: $container.locator(".btn:not(.active)").first()
234
+ $true: $container.locator("input").nth(0), // eslint-disable-line protractor/use-first-last
235
+ $false: $container.locator("input").nth(1),
236
+ $undefined: this.$locate(str).locator("input").nth(2),
237
+ $active: $container.locator("input[checked]"),
238
+ $nonactive: $container.locator("input:not([checked])").first()
239
239
  };
240
240
  }
241
241
  $getInputWidget(str) {