@limetech/lime-elements 40.12.2 → 40.13.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/CHANGELOG.md +21 -0
- package/dist/cjs/{checkbox.template-_WUgtMJy.js → checkbox.template-CNJ29zDR.js} +1 -1
- package/dist/cjs/drag-to-reorder-BP5YkPuu.js +3092 -0
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-checkbox.cjs.entry.js +1 -1
- package/dist/cjs/limel-chip-set.cjs.entry.js +5 -2
- package/dist/cjs/{limel-scroller.cjs.entry.js → limel-drag-handle_2.cjs.entry.js} +38 -0
- package/dist/cjs/limel-file-viewer.cjs.entry.js +64 -55
- package/dist/cjs/limel-form.cjs.entry.js +539 -3171
- package/dist/cjs/limel-list-item.cjs.entry.js +1 -1
- package/dist/cjs/limel-select.cjs.entry.js +2 -2
- package/dist/cjs/limel-slider.cjs.entry.js +4 -4
- package/dist/cjs/limel-switch.cjs.entry.js +1 -1
- package/dist/cjs/limel-tab-bar.cjs.entry.js +120 -16
- package/dist/cjs/limel-tab-panel.cjs.entry.js +7 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/checkbox/checkbox.template.js +1 -1
- package/dist/collection/components/chip-set/chip-set.js +5 -2
- package/dist/collection/components/form/form.css +59 -0
- package/dist/collection/components/form/form.js +49 -2
- package/dist/collection/components/form/show-field.js +116 -0
- package/dist/collection/components/form/templates/array-field-collapsible-item.js +1 -0
- package/dist/collection/components/form/templates/array-field-item.js +5 -0
- package/dist/collection/components/form/templates/array-field.js +20 -158
- package/dist/collection/components/form/templates/field.js +17 -0
- package/dist/collection/components/select/select.template.js +2 -2
- package/dist/collection/components/slider/slider.js +4 -4
- package/dist/collection/components/switch/switch.js +1 -1
- package/dist/collection/components/tab-bar/tab-bar.css +68 -22
- package/dist/collection/components/tab-bar/tab-bar.js +131 -4
- package/dist/collection/components/tab-bar/tabs.js +23 -0
- package/dist/collection/components/tab-panel/tab-panel.js +54 -1
- package/dist/collection/util/drag-to-reorder.js +116 -0
- package/dist/esm/{_baseForOwn-D5j7fHlv.js → _baseForOwn-vKFWmmaS.js} +1 -1
- package/dist/esm/{_baseIsEqual-CyF4P8uM.js → _baseIsEqual-B_JRErB0.js} +1 -1
- package/dist/esm/{_baseIteratee-CpbczHY1.js → _baseIteratee-CVz7aes3.js} +1 -1
- package/dist/esm/{_baseSet-Cw2IE6ZF.js → _baseSet-BUAq8NGm.js} +1 -1
- package/dist/esm/{checkbox.template-BFL4SMzl.js → checkbox.template-CAja2LKZ.js} +1 -1
- package/dist/esm/{cloneDeep-DAbOtbs8.js → cloneDeep-Bcll-3f8.js} +1 -1
- package/dist/esm/drag-to-reorder-BXmXIwCa.js +3086 -0
- package/dist/esm/{isEqual-CE83yS8T.js → isEqual-C-pK6AAU.js} +1 -1
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-checkbox.entry.js +1 -1
- package/dist/esm/limel-chip-set.entry.js +7 -4
- package/dist/esm/limel-dialog.entry.js +2 -2
- package/dist/esm/{limel-scroller.entry.js → limel-drag-handle_2.entry.js} +38 -1
- package/dist/esm/limel-file-dropzone_2.entry.js +3 -3
- package/dist/esm/limel-file-viewer.entry.js +64 -55
- package/dist/esm/limel-form.entry.js +536 -3168
- package/dist/esm/limel-list-item.entry.js +1 -1
- package/dist/esm/limel-prosemirror-adapter.entry.js +3 -3
- package/dist/esm/limel-select.entry.js +2 -2
- package/dist/esm/limel-slider.entry.js +4 -4
- package/dist/esm/limel-switch.entry.js +1 -1
- package/dist/esm/limel-tab-bar.entry.js +117 -13
- package/dist/esm/limel-tab-panel.entry.js +7 -1
- package/dist/esm/limel-table.entry.js +4 -4
- package/dist/esm/loader.js +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-35bc5ed2.entry.js → p-13f2afc9.entry.js} +1 -1
- package/dist/lime-elements/{p-43c36a02.entry.js → p-268b4da1.entry.js} +1 -1
- package/dist/lime-elements/p-3b6d4934.entry.js +1 -0
- package/dist/lime-elements/p-55b56742.entry.js +1 -0
- package/dist/lime-elements/p-67767276.entry.js +1 -0
- package/dist/lime-elements/{p-fb15f608.entry.js → p-6d65ee35.entry.js} +1 -1
- package/dist/lime-elements/p-714ab66d.entry.js +16 -0
- package/dist/lime-elements/p-7ae71c13.entry.js +1 -0
- package/dist/lime-elements/{p-c492af1b.entry.js → p-828558cf.entry.js} +1 -1
- package/dist/lime-elements/p-95322502.entry.js +1 -0
- package/dist/lime-elements/p-BAqAt5It.js +1 -0
- package/dist/lime-elements/{p-1qqvOwzC.js → p-Cadm4-WZ.js} +1 -1
- package/dist/lime-elements/p-Cu9Ji1iJ.js +1 -0
- package/dist/lime-elements/{p-BY5NjPj8.js → p-D9Ezd43A.js} +1 -1
- package/dist/lime-elements/{p-nVemxylR.js → p-DFu1q54T.js} +1 -1
- package/dist/lime-elements/p-DdnBHYb-.js +7 -0
- package/dist/lime-elements/{p-B6-3WXAv.js → p-DeIIlp6m.js} +1 -1
- package/dist/lime-elements/p-Dq0uNStc.js +1 -0
- package/dist/lime-elements/p-b5bb124f.entry.js +1 -0
- package/dist/lime-elements/{p-9168793f.entry.js → p-bd58c761.entry.js} +1 -1
- package/dist/lime-elements/{p-458c131f.entry.js → p-c7e34502.entry.js} +1 -1
- package/dist/lime-elements/p-f4c9940e.entry.js +23 -0
- package/dist/lime-elements/p-fb526a05.entry.js +1 -0
- package/dist/types/components/chip-set/chip-set.d.ts +1 -0
- package/dist/types/components/form/form.d.ts +15 -0
- package/dist/types/components/form/show-field.d.ts +9 -0
- package/dist/types/components/form/templates/array-context.d.ts +4 -0
- package/dist/types/components/form/templates/array-field-collapsible-item.d.ts +5 -0
- package/dist/types/components/form/templates/array-field.d.ts +3 -13
- package/dist/types/components/form/templates/field.d.ts +1 -0
- package/dist/types/components/form/templates/object-field.d.ts +2 -2
- package/dist/types/components/tab-bar/tab-bar.d.ts +25 -0
- package/dist/types/components/tab-bar/tab.types.d.ts +18 -0
- package/dist/types/components/tab-bar/tabs.d.ts +12 -1
- package/dist/types/components/tab-panel/tab-panel.d.ts +18 -1
- package/dist/types/components.d.ts +80 -2
- package/dist/types/util/drag-to-reorder.d.ts +65 -0
- package/package.json +4 -4
- package/dist/cjs/isArrayLikeObject-U2Ddbw4n.js +0 -326
- package/dist/cjs/limel-drag-handle.cjs.entry.js +0 -42
- package/dist/esm/isArrayLikeObject-CJB8HbtH.js +0 -321
- package/dist/esm/limel-drag-handle.entry.js +0 -40
- package/dist/lime-elements/p-06850418.entry.js +0 -1
- package/dist/lime-elements/p-262a3d66.entry.js +0 -1
- package/dist/lime-elements/p-49eb2f64.entry.js +0 -13
- package/dist/lime-elements/p-646731b6.entry.js +0 -1
- package/dist/lime-elements/p-89d75e9c.entry.js +0 -1
- package/dist/lime-elements/p-8d567e42.entry.js +0 -1
- package/dist/lime-elements/p-92fab922.entry.js +0 -23
- package/dist/lime-elements/p-BQeIv4aA.js +0 -1
- package/dist/lime-elements/p-COdirXg1.js +0 -1
- package/dist/lime-elements/p-ORUNaKm-.js +0 -1
- package/dist/lime-elements/p-a5246373.entry.js +0 -1
- package/dist/lime-elements/p-b609552e.entry.js +0 -1
- package/dist/lime-elements/p-bf849dc9.entry.js +0 -1
- package/dist/lime-elements/p-pk5KIdKH.js +0 -1
|
@@ -434,4 +434,63 @@ limel-markdown {
|
|
|
434
434
|
}
|
|
435
435
|
.rjsf-field .rjsf-field .rjsf-field .rjsf-field .rjsf-field .rjsf-field h1:before {
|
|
436
436
|
display: none;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.limel-form-field--highlighted {
|
|
440
|
+
position: relative;
|
|
441
|
+
}
|
|
442
|
+
.limel-form-field--highlighted::after {
|
|
443
|
+
content: "";
|
|
444
|
+
position: absolute;
|
|
445
|
+
inset: -0.25rem;
|
|
446
|
+
border-radius: 0.5rem;
|
|
447
|
+
pointer-events: none;
|
|
448
|
+
animation-name: highlight-field-background, highlight-field-shadow;
|
|
449
|
+
animation-duration: 6s;
|
|
450
|
+
animation-delay: 0.1s, 0.3s;
|
|
451
|
+
animation-timing-function: ease-in-out;
|
|
452
|
+
}
|
|
453
|
+
@media (prefers-reduced-motion) {
|
|
454
|
+
.limel-form-field--highlighted::after {
|
|
455
|
+
animation-duration: 4s;
|
|
456
|
+
animation-delay: 0s;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@keyframes highlight-field-background {
|
|
461
|
+
0% {
|
|
462
|
+
background-color: transparent;
|
|
463
|
+
}
|
|
464
|
+
12.5% {
|
|
465
|
+
background-color: color-mix(in srgb, var(--lime-primary-color, var(--limel-theme-primary-color)), transparent 65%);
|
|
466
|
+
}
|
|
467
|
+
45%, 100% {
|
|
468
|
+
background-color: transparent;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
@keyframes highlight-field-shadow {
|
|
472
|
+
0%, 25%, 50% {
|
|
473
|
+
box-shadow: none;
|
|
474
|
+
}
|
|
475
|
+
12.5%, 37.5% {
|
|
476
|
+
box-shadow: var(--shadow-depth-8-focused);
|
|
477
|
+
}
|
|
478
|
+
62.5%, 72.5% {
|
|
479
|
+
box-shadow: var(--shadow-depth-8-focused);
|
|
480
|
+
animation-timing-function: ease-out;
|
|
481
|
+
}
|
|
482
|
+
100% {
|
|
483
|
+
box-shadow: none;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
@media (prefers-reduced-motion) {
|
|
487
|
+
@keyframes highlight-field-shadow {
|
|
488
|
+
0%, 25% {
|
|
489
|
+
box-shadow: var(--shadow-depth-8-focused);
|
|
490
|
+
animation-timing-function: ease-out;
|
|
491
|
+
}
|
|
492
|
+
100% {
|
|
493
|
+
box-shadow: none;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
437
496
|
}
|
|
@@ -20,7 +20,8 @@ import { ArrayField as CustomArrayField } from "./fields/array-field";
|
|
|
20
20
|
import { ObjectField as CustomObjectField } from "./fields/object-field";
|
|
21
21
|
import { widgets } from "./widgets";
|
|
22
22
|
import { rjsfValidator } from "./validator";
|
|
23
|
-
import { mapValues } from "lodash-es";
|
|
23
|
+
import { isEqual, mapValues } from "lodash-es";
|
|
24
|
+
import { showField } from "./show-field";
|
|
24
25
|
/**
|
|
25
26
|
* @exampleComponent limel-example-form
|
|
26
27
|
* @exampleComponent limel-example-nested-form
|
|
@@ -38,6 +39,7 @@ import { mapValues } from "lodash-es";
|
|
|
38
39
|
* @exampleComponent limel-example-form-row-layout
|
|
39
40
|
* @exampleComponent limel-example-builtin-field-types-form
|
|
40
41
|
* @exampleComponent limel-example-code-editor-form
|
|
42
|
+
* @exampleComponent limel-example-form-highlighted-field
|
|
41
43
|
*/
|
|
42
44
|
export class Form {
|
|
43
45
|
constructor() {
|
|
@@ -66,6 +68,13 @@ export class Form {
|
|
|
66
68
|
}
|
|
67
69
|
componentDidLoad() {
|
|
68
70
|
this.initialize();
|
|
71
|
+
this.showHighlightedField();
|
|
72
|
+
}
|
|
73
|
+
handleHighlightedFieldChange(newPath, oldPath) {
|
|
74
|
+
// A host that renders again passes an equal path in a new array.
|
|
75
|
+
if (!isEqual(newPath, oldPath)) {
|
|
76
|
+
this.showHighlightedField();
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
initialize() {
|
|
71
80
|
if (this.initialized || !this.host.shadowRoot.querySelector('.root')) {
|
|
@@ -87,7 +96,7 @@ export class Form {
|
|
|
87
96
|
this.initialized = false;
|
|
88
97
|
}
|
|
89
98
|
render() {
|
|
90
|
-
return h("div", { key: '
|
|
99
|
+
return h("div", { key: 'ffbfb1d5d0377c01a29740b7a08fdfb3532e470f', class: "root" });
|
|
91
100
|
}
|
|
92
101
|
reactRender() {
|
|
93
102
|
if (!this.root) {
|
|
@@ -134,6 +143,13 @@ export class Form {
|
|
|
134
143
|
},
|
|
135
144
|
}, []));
|
|
136
145
|
}
|
|
146
|
+
showHighlightedField() {
|
|
147
|
+
if (!this.highlightedField) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const root = this.host.shadowRoot.querySelector('.root');
|
|
151
|
+
void showField(root, this.highlightedField);
|
|
152
|
+
}
|
|
137
153
|
handleChange(event) {
|
|
138
154
|
this.change.emit(event.formData);
|
|
139
155
|
}
|
|
@@ -386,6 +402,31 @@ export class Form {
|
|
|
386
402
|
"reflect": false,
|
|
387
403
|
"attribute": "reveal-errors",
|
|
388
404
|
"defaultValue": "false"
|
|
405
|
+
},
|
|
406
|
+
"highlightedField": {
|
|
407
|
+
"type": "unknown",
|
|
408
|
+
"mutable": false,
|
|
409
|
+
"complexType": {
|
|
410
|
+
"original": "Array<string | number>",
|
|
411
|
+
"resolved": "(string | number)[]",
|
|
412
|
+
"references": {
|
|
413
|
+
"Array": {
|
|
414
|
+
"location": "global",
|
|
415
|
+
"id": "global::Array"
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
"required": false,
|
|
420
|
+
"optional": true,
|
|
421
|
+
"docs": {
|
|
422
|
+
"tags": [{
|
|
423
|
+
"name": "beta",
|
|
424
|
+
"text": undefined
|
|
425
|
+
}],
|
|
426
|
+
"text": "The path in the value of a field to take the user to, such as\n`['heroes', 2, 'name']`. When it changes, the form opens the sections\nand list items the field is in, scrolls it into view, focuses it and\nbriefly highlights it.\n\nIf the path goes further than the form can show, such as into a custom\ncomponent, the closest field above the end of the path is shown. To show\nthe same field again, set it to `undefined` first."
|
|
427
|
+
},
|
|
428
|
+
"getter": false,
|
|
429
|
+
"setter": false
|
|
389
430
|
}
|
|
390
431
|
};
|
|
391
432
|
}
|
|
@@ -430,4 +471,10 @@ export class Form {
|
|
|
430
471
|
}];
|
|
431
472
|
}
|
|
432
473
|
static get elementRef() { return "host"; }
|
|
474
|
+
static get watchers() {
|
|
475
|
+
return [{
|
|
476
|
+
"propName": "highlightedField",
|
|
477
|
+
"methodName": "handleHighlightedFieldChange"
|
|
478
|
+
}];
|
|
479
|
+
}
|
|
433
480
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { flushSync } from "react-dom";
|
|
2
|
+
import { toSchemaPath } from "./templates/object-field";
|
|
3
|
+
import { getFocusTarget } from "../../util/get-focus-target";
|
|
4
|
+
const HIGHLIGHT_CLASS = 'limel-form-field--highlighted';
|
|
5
|
+
const STABLE_FRAMES = 3;
|
|
6
|
+
const MAX_SETTLE_MS = 2000;
|
|
7
|
+
const MAX_RENDER_FRAMES = 30;
|
|
8
|
+
/**
|
|
9
|
+
* Open what is closed above a field, scroll it into view, focus it and
|
|
10
|
+
* highlight it.
|
|
11
|
+
*
|
|
12
|
+
* @param root - the element the form is rendered in
|
|
13
|
+
* @param path - the path of the field in the value of the form
|
|
14
|
+
*/
|
|
15
|
+
export async function showField(root, path) {
|
|
16
|
+
if (!(await waitUntilRendered(root, path))) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let target;
|
|
20
|
+
for (let depth = 1; depth <= path.length; depth++) {
|
|
21
|
+
const element = findByPath(root, path.slice(0, depth));
|
|
22
|
+
if (!element) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
target = element;
|
|
26
|
+
openSectionsAround(root, element);
|
|
27
|
+
}
|
|
28
|
+
if (!target) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// The components inside the field have to be drawn, and the sections
|
|
32
|
+
// around it opened, before the field is where it will stay.
|
|
33
|
+
const focusTarget = await getFocusTarget(target);
|
|
34
|
+
await waitForLayoutToSettle(target);
|
|
35
|
+
target.scrollIntoView({
|
|
36
|
+
block: 'center',
|
|
37
|
+
behavior: prefersReducedMotion() ? 'instant' : 'smooth',
|
|
38
|
+
});
|
|
39
|
+
focusTarget === null || focusTarget === void 0 ? void 0 : focusTarget.focus({ preventScroll: true });
|
|
40
|
+
await waitForLayoutToSettle(target);
|
|
41
|
+
highlight(target);
|
|
42
|
+
}
|
|
43
|
+
function findByPath(root, path) {
|
|
44
|
+
const schemaPath = toSchemaPath(path.map(String));
|
|
45
|
+
return root.querySelector(`[data-schema-path="${CSS.escape(schemaPath)}"]`);
|
|
46
|
+
}
|
|
47
|
+
// The form is drawn a moment after its value or schema is set, so give it a few
|
|
48
|
+
// frames to show the start of the path.
|
|
49
|
+
async function waitUntilRendered(root, path) {
|
|
50
|
+
for (let frame = 0; frame < MAX_RENDER_FRAMES; frame++) {
|
|
51
|
+
if (findByPath(root, path.slice(0, 1))) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
await nextFrame();
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
function openSectionsAround(root, element) {
|
|
59
|
+
for (let node = element; node && node !== root; node = node.parentElement) {
|
|
60
|
+
if (isClosedSection(node)) {
|
|
61
|
+
open(node);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isClosedSection(element) {
|
|
66
|
+
return (element.tagName === 'LIMEL-COLLAPSIBLE-SECTION' &&
|
|
67
|
+
!element.isOpen);
|
|
68
|
+
}
|
|
69
|
+
// A collapsed list item renders its fields only once its section says it has
|
|
70
|
+
// been opened, and they have to be in place before the next step is looked up.
|
|
71
|
+
function open(section) {
|
|
72
|
+
section.isOpen = true;
|
|
73
|
+
flushSync(() => {
|
|
74
|
+
section.dispatchEvent(new CustomEvent('open'));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Wait until the field has a size and has stayed in place for a few frames, so
|
|
78
|
+
// it is not scrolled to while the form still grows, nor flashed while scrolling.
|
|
79
|
+
async function waitForLayoutToSettle(element) {
|
|
80
|
+
let previous = '';
|
|
81
|
+
let stableFrames = 0;
|
|
82
|
+
const deadline = performance.now() + MAX_SETTLE_MS;
|
|
83
|
+
while (performance.now() < deadline) {
|
|
84
|
+
await nextFrame();
|
|
85
|
+
const { top, height } = element.getBoundingClientRect();
|
|
86
|
+
const current = `${top}:${height}`;
|
|
87
|
+
const isStill = height > 0 && current === previous;
|
|
88
|
+
stableFrames = isStill ? stableFrames + 1 : 0;
|
|
89
|
+
if (stableFrames === STABLE_FRAMES) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
previous = current;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function highlight(element) {
|
|
96
|
+
// Removed for a frame first, so showing the same field again restarts it.
|
|
97
|
+
element.classList.remove(HIGHLIGHT_CLASS);
|
|
98
|
+
const removeHighlight = (event) => {
|
|
99
|
+
// Animations of what the field holds end here too.
|
|
100
|
+
if (event.animationName !== 'highlight-field-shadow') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
element.classList.remove(HIGHLIGHT_CLASS);
|
|
104
|
+
element.removeEventListener('animationend', removeHighlight);
|
|
105
|
+
};
|
|
106
|
+
requestAnimationFrame(() => {
|
|
107
|
+
element.classList.add(HIGHLIGHT_CLASS);
|
|
108
|
+
element.addEventListener('animationend', removeHighlight);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function prefersReducedMotion() {
|
|
112
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
113
|
+
}
|
|
114
|
+
function nextFrame() {
|
|
115
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
116
|
+
}
|
|
@@ -54,6 +54,7 @@ export class CollapsibleItemTemplate extends React.Component {
|
|
|
54
54
|
this.section = section;
|
|
55
55
|
},
|
|
56
56
|
'is-open': this.state.isOpen,
|
|
57
|
+
'data-schema-path': this.props.schemaPath,
|
|
57
58
|
'data-reorder-id': String(this.props.index),
|
|
58
59
|
'data-reorderable': this.props.allowItemReorder
|
|
59
60
|
? 'true'
|
|
@@ -3,6 +3,7 @@ import { isObjectType } from "../schema";
|
|
|
3
3
|
import { CollapsibleItemTemplate } from "./array-field-collapsible-item";
|
|
4
4
|
import { SimpleItemTemplate } from "./array-field-simple-item";
|
|
5
5
|
import { ArrayFieldContext, ArrayItemErrorsContext } from "./array-context";
|
|
6
|
+
import { toSchemaPath } from "./object-field";
|
|
6
7
|
export const ArrayFieldItemTemplate = (props) => {
|
|
7
8
|
const arrayContext = useContext(ArrayFieldContext);
|
|
8
9
|
const itemErrors = useContext(ArrayItemErrorsContext);
|
|
@@ -26,6 +27,10 @@ export const ArrayFieldItemTemplate = (props) => {
|
|
|
26
27
|
allowItemReorder: allowItemReorder,
|
|
27
28
|
errorSchema: itemErrors === null || itemErrors === void 0 ? void 0 : itemErrors[props.index],
|
|
28
29
|
revealErrors: (formContext === null || formContext === void 0 ? void 0 : formContext.revealErrors) === true,
|
|
30
|
+
schemaPath: toSchemaPath([
|
|
31
|
+
...arrayContext.schemaPath,
|
|
32
|
+
String(props.index),
|
|
33
|
+
]),
|
|
29
34
|
}, props.children);
|
|
30
35
|
}
|
|
31
36
|
return React.createElement(SimpleItemTemplate, {
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { renderDescription, renderTitle } from "./common";
|
|
3
|
-
import
|
|
3
|
+
import { toSchemaPath } from "./object-field";
|
|
4
|
+
import { DragToReorder } from "../../../util/drag-to-reorder";
|
|
4
5
|
import { ArrayFieldContext } from "./array-context";
|
|
5
6
|
export const ARRAY_REORDER_EVENT = 'arrayReorder';
|
|
6
|
-
const DRAG_HANDLE_SELECTOR = '[data-drag-handle]';
|
|
7
7
|
const DRAGGABLE_ITEM_SELECTOR = '.array-item[data-reorderable="true"]';
|
|
8
|
-
const DEFAULT_CONTAINER_CLASS = 'has-an-item-which-is-being-dragged';
|
|
9
|
-
const DEFAULT_DROP_ELEVATION_CLASS = 'is-elevated';
|
|
10
|
-
const DROP_ELEVATION_DURATION = 1000;
|
|
11
|
-
const TOUCH_DRAG_DELAY_MS = 200; // Adds a short hold on touch (long-press) so scroll gestures do not reorder items.
|
|
12
8
|
export class ArrayFieldTemplate extends React.Component {
|
|
13
9
|
constructor(props) {
|
|
14
10
|
super(props);
|
|
@@ -21,68 +17,28 @@ export class ArrayFieldTemplate extends React.Component {
|
|
|
21
17
|
if (this.container === element) {
|
|
22
18
|
return;
|
|
23
19
|
}
|
|
24
|
-
this.
|
|
20
|
+
this.dragToReorder.teardown();
|
|
25
21
|
this.container = element !== null && element !== void 0 ? element : undefined;
|
|
26
22
|
this.setupDragController();
|
|
27
23
|
};
|
|
28
|
-
this.
|
|
29
|
-
if (!this.canReorderItems()) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
this.dragSnapshot = [...this.state.order];
|
|
33
|
-
this.draggedItemIndex = this.getReorderId(event.item);
|
|
34
|
-
if (this.container) {
|
|
35
|
-
this.container.classList.add(DEFAULT_CONTAINER_CLASS);
|
|
36
|
-
}
|
|
37
|
-
if (event.item instanceof HTMLElement) {
|
|
38
|
-
this.applyDropElevation(event.item);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
this.handleSortEnd = (event) => {
|
|
24
|
+
this.handleReorder = ({ order, fromIndex, toIndex, }) => {
|
|
42
25
|
var _a;
|
|
43
|
-
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (this.container) {
|
|
47
|
-
this.container.classList.remove(DEFAULT_CONTAINER_CLASS);
|
|
48
|
-
}
|
|
49
|
-
const snapshot = this.dragSnapshot;
|
|
50
|
-
const draggedItemIndex = this.draggedItemIndex;
|
|
51
|
-
this.dragSnapshot = undefined;
|
|
52
|
-
this.draggedItemIndex = undefined;
|
|
53
|
-
if (event.item instanceof HTMLElement) {
|
|
54
|
-
this.dropElevationTarget = event.item;
|
|
55
|
-
}
|
|
56
|
-
const finalOrder = this.readOrderFromDom();
|
|
57
|
-
if (!this.arraysEqual(this.state.order, finalOrder)) {
|
|
58
|
-
this.setState({ order: finalOrder });
|
|
59
|
-
}
|
|
60
|
-
if (snapshot === undefined ||
|
|
61
|
-
draggedItemIndex === undefined ||
|
|
62
|
-
!(event.item instanceof HTMLElement)) {
|
|
63
|
-
this.scheduleDropElevationRemoval();
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const fromPosition = snapshot.indexOf(draggedItemIndex);
|
|
67
|
-
const toPosition = finalOrder.indexOf(draggedItemIndex);
|
|
68
|
-
if (fromPosition === -1 ||
|
|
69
|
-
toPosition === -1 ||
|
|
70
|
-
fromPosition === toPosition) {
|
|
71
|
-
this.scheduleDropElevationRemoval();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
26
|
+
this.setState({ order: order.map(Number) });
|
|
74
27
|
(_a = this.container) === null || _a === void 0 ? void 0 : _a.dispatchEvent(new CustomEvent(ARRAY_REORDER_EVENT, {
|
|
75
28
|
bubbles: true,
|
|
76
|
-
detail: { fromIndex
|
|
29
|
+
detail: { fromIndex, toIndex },
|
|
77
30
|
}));
|
|
78
|
-
this.scheduleDropElevationRemoval();
|
|
79
31
|
};
|
|
80
32
|
this.state = {
|
|
81
33
|
order: this.extractIndices(props.items),
|
|
82
34
|
};
|
|
35
|
+
this.dragToReorder = new DragToReorder({
|
|
36
|
+
draggable: DRAGGABLE_ITEM_SELECTOR,
|
|
37
|
+
onReorder: this.handleReorder,
|
|
38
|
+
});
|
|
83
39
|
}
|
|
84
40
|
componentWillUnmount() {
|
|
85
|
-
this.
|
|
41
|
+
this.dragToReorder.teardown();
|
|
86
42
|
}
|
|
87
43
|
componentDidMount() {
|
|
88
44
|
this.setupDragController();
|
|
@@ -151,47 +107,10 @@ export class ArrayFieldTemplate extends React.Component {
|
|
|
151
107
|
}
|
|
152
108
|
setupDragController() {
|
|
153
109
|
if (!this.container || !this.canReorderItems()) {
|
|
154
|
-
this.
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const reorderableItems = this.container.querySelectorAll(`:scope > ${DRAGGABLE_ITEM_SELECTOR}`);
|
|
158
|
-
if (reorderableItems.length < 2) {
|
|
159
|
-
this.teardownDragController();
|
|
110
|
+
this.dragToReorder.teardown();
|
|
160
111
|
return;
|
|
161
112
|
}
|
|
162
|
-
|
|
163
|
-
this.sortable.option('handle', DRAG_HANDLE_SELECTOR);
|
|
164
|
-
this.sortable.option('draggable', DRAGGABLE_ITEM_SELECTOR);
|
|
165
|
-
this.sortable.option('disabled', false);
|
|
166
|
-
this.sortable.option('delay', TOUCH_DRAG_DELAY_MS);
|
|
167
|
-
this.sortable.option('delayOnTouchOnly', true);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
this.sortable = Sortable.create(this.container, {
|
|
171
|
-
animation: 150,
|
|
172
|
-
handle: DRAG_HANDLE_SELECTOR,
|
|
173
|
-
draggable: DRAGGABLE_ITEM_SELECTOR,
|
|
174
|
-
delay: TOUCH_DRAG_DELAY_MS,
|
|
175
|
-
delayOnTouchOnly: true,
|
|
176
|
-
onStart: this.handleSortStart,
|
|
177
|
-
onEnd: this.handleSortEnd,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
teardownDragController() {
|
|
181
|
-
if (this.sortable) {
|
|
182
|
-
this.sortable.destroy();
|
|
183
|
-
this.sortable = undefined;
|
|
184
|
-
}
|
|
185
|
-
this.clearDropElevationTimer();
|
|
186
|
-
if (this.dropElevationTarget) {
|
|
187
|
-
this.dropElevationTarget.classList.remove(DEFAULT_DROP_ELEVATION_CLASS);
|
|
188
|
-
this.dropElevationTarget = undefined;
|
|
189
|
-
}
|
|
190
|
-
if (this.container) {
|
|
191
|
-
this.container.classList.remove(DEFAULT_CONTAINER_CLASS);
|
|
192
|
-
}
|
|
193
|
-
this.dragSnapshot = undefined;
|
|
194
|
-
this.draggedItemIndex = undefined;
|
|
113
|
+
this.dragToReorder.setup(this.container);
|
|
195
114
|
}
|
|
196
115
|
canReorderItems() {
|
|
197
116
|
if (this.props.disabled || this.props.readonly) {
|
|
@@ -201,67 +120,6 @@ export class ArrayFieldTemplate extends React.Component {
|
|
|
201
120
|
const limeOptions = (schema === null || schema === void 0 ? void 0 : schema.lime) || {};
|
|
202
121
|
return limeOptions.allowItemReorder !== false;
|
|
203
122
|
}
|
|
204
|
-
readOrderFromDom() {
|
|
205
|
-
if (!this.container) {
|
|
206
|
-
return [];
|
|
207
|
-
}
|
|
208
|
-
// Only read the order of the direct children of this array field.
|
|
209
|
-
// Nested array fields may render `.array-item` elements inside items,
|
|
210
|
-
// and those must not affect the parent order.
|
|
211
|
-
const items = [...this.container.children].filter((element) => {
|
|
212
|
-
var _a, _b;
|
|
213
|
-
return Boolean((_b = (_a = element === null || element === void 0 ? void 0 : element.classList) === null || _a === void 0 ? void 0 : _a.contains) === null || _b === void 0 ? void 0 : _b.call(_a, 'array-item'));
|
|
214
|
-
});
|
|
215
|
-
const order = [];
|
|
216
|
-
for (const element of items) {
|
|
217
|
-
const index = this.getReorderId(element);
|
|
218
|
-
if (index !== undefined) {
|
|
219
|
-
order.push(index);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return order;
|
|
223
|
-
}
|
|
224
|
-
getReorderId(element) {
|
|
225
|
-
var _a;
|
|
226
|
-
if (!element) {
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
const value = (_a = element === null || element === void 0 ? void 0 : element.dataset) === null || _a === void 0 ? void 0 : _a.reorderId;
|
|
230
|
-
if (value === undefined) {
|
|
231
|
-
return undefined;
|
|
232
|
-
}
|
|
233
|
-
const parsed = Number.parseInt(value, 10);
|
|
234
|
-
return Number.isNaN(parsed) ? undefined : parsed;
|
|
235
|
-
}
|
|
236
|
-
applyDropElevation(item) {
|
|
237
|
-
if (this.dropElevationTarget && this.dropElevationTarget !== item) {
|
|
238
|
-
this.dropElevationTarget.classList.remove(DEFAULT_DROP_ELEVATION_CLASS);
|
|
239
|
-
}
|
|
240
|
-
this.clearDropElevationTimer();
|
|
241
|
-
item.classList.add(DEFAULT_DROP_ELEVATION_CLASS);
|
|
242
|
-
this.dropElevationTarget = item;
|
|
243
|
-
}
|
|
244
|
-
scheduleDropElevationRemoval() {
|
|
245
|
-
if (!this.dropElevationTarget) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const target = this.dropElevationTarget;
|
|
249
|
-
this.clearDropElevationTimer();
|
|
250
|
-
this.dropElevationTimeout = setTimeout(() => {
|
|
251
|
-
target.classList.remove(DEFAULT_DROP_ELEVATION_CLASS);
|
|
252
|
-
if (this.dropElevationTarget === target) {
|
|
253
|
-
this.dropElevationTarget = undefined;
|
|
254
|
-
}
|
|
255
|
-
this.dropElevationTimeout = undefined;
|
|
256
|
-
}, DROP_ELEVATION_DURATION);
|
|
257
|
-
}
|
|
258
|
-
clearDropElevationTimer() {
|
|
259
|
-
if (this.dropElevationTimeout === undefined) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
clearTimeout(this.dropElevationTimeout);
|
|
263
|
-
this.dropElevationTimeout = undefined;
|
|
264
|
-
}
|
|
265
123
|
arraysEqual(a, b) {
|
|
266
124
|
if (a.length !== b.length) {
|
|
267
125
|
return false;
|
|
@@ -279,17 +137,21 @@ export class ArrayFieldTemplate extends React.Component {
|
|
|
279
137
|
return (items !== null && items !== void 0 ? items : []).map((_, index) => index);
|
|
280
138
|
}
|
|
281
139
|
getContextValue() {
|
|
282
|
-
var _a;
|
|
140
|
+
var _a, _b, _c;
|
|
283
141
|
const schema = this.props.schema;
|
|
284
142
|
const formData = ((_a = this.props.formData) !== null && _a !== void 0 ? _a : []);
|
|
143
|
+
const schemaPath = ((_c = (_b = this.props.fieldPathId) === null || _b === void 0 ? void 0 : _b.path) !== null && _c !== void 0 ? _c : []).map(String);
|
|
144
|
+
const path = toSchemaPath(schemaPath);
|
|
285
145
|
if (this.cachedContextValue &&
|
|
286
146
|
this.cachedSchema === schema &&
|
|
287
|
-
this.cachedFormData === formData
|
|
147
|
+
this.cachedFormData === formData &&
|
|
148
|
+
this.cachedPath === path) {
|
|
288
149
|
return this.cachedContextValue;
|
|
289
150
|
}
|
|
290
151
|
this.cachedSchema = schema;
|
|
291
152
|
this.cachedFormData = formData;
|
|
292
|
-
this.
|
|
153
|
+
this.cachedPath = path;
|
|
154
|
+
this.cachedContextValue = { arraySchema: schema, formData, schemaPath };
|
|
293
155
|
return this.cachedContextValue;
|
|
294
156
|
}
|
|
295
157
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { isObjectType } from "../schema";
|
|
3
|
+
import { toSchemaPath } from "./object-field";
|
|
4
|
+
import { isCustomObjectSchema } from "../fields/field-helpers";
|
|
3
5
|
export const FieldTemplate = (props) => {
|
|
4
6
|
let classNames = props.classNames;
|
|
5
7
|
const colSpan = getColSpan(props.schema);
|
|
@@ -17,8 +19,23 @@ export const FieldTemplate = (props) => {
|
|
|
17
19
|
return React.createElement('div', {
|
|
18
20
|
className: classNames,
|
|
19
21
|
style: style,
|
|
22
|
+
'data-schema-path': getFieldSchemaPath(props),
|
|
20
23
|
}, props.children);
|
|
21
24
|
};
|
|
25
|
+
// An object marks its own layout wrapper, unless a component or the code
|
|
26
|
+
// editor draws it instead. It lets a host find the element of one field by its path.
|
|
27
|
+
function getFieldSchemaPath(props) {
|
|
28
|
+
var _a;
|
|
29
|
+
if (isObjectType(props.schema) && hasOwnLayout(props.schema)) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const path = (_a = props.fieldPathId) === null || _a === void 0 ? void 0 : _a.path;
|
|
33
|
+
return path ? toSchemaPath(path.map(String)) : undefined;
|
|
34
|
+
}
|
|
35
|
+
function hasOwnLayout(schema) {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
return !((_b = (_a = schema.lime) === null || _a === void 0 ? void 0 : _a.component) === null || _b === void 0 ? void 0 : _b.name) && !isCustomObjectSchema(schema);
|
|
38
|
+
}
|
|
22
39
|
function getColSpan(schema) {
|
|
23
40
|
var _a;
|
|
24
41
|
const layout = (_a = schema.lime) === null || _a === void 0 ? void 0 : _a.layout;
|
|
@@ -35,7 +35,7 @@ export const SelectTemplate = (props) => {
|
|
|
35
35
|
};
|
|
36
36
|
return [
|
|
37
37
|
h("div", { class: fieldClassList }, h("limel-notched-outline", { class: classList, labelId: TRIGGER_ID, labelElementId: LABEL_ID, label: props.label, required: props.required, invalid: !isValid, disabled: props.disabled, readonly: props.readonly, hasValue: hasValue, hasFloatingLabel: floatLabelAbove(props) }, h(SelectValue, Object.assign({}, props, { hasValue: hasValue, isValid: isValid, hasEmptyText: hasEmptyText }))), h(HelperText, { text: props.helperText, isValid: isValid })),
|
|
38
|
-
h(SelectDropdown, Object.assign({}, props)),
|
|
38
|
+
h(SelectDropdown, Object.assign({}, props, { isValid: isValid })),
|
|
39
39
|
];
|
|
40
40
|
};
|
|
41
41
|
const getDescribedBy = (props) => {
|
|
@@ -97,7 +97,7 @@ const NativeDropdown = (props) => {
|
|
|
97
97
|
const options = props.options
|
|
98
98
|
.filter((option) => !('separator' in option))
|
|
99
99
|
.map(renderOption(props.value));
|
|
100
|
-
return (h("select", { required: props.required, "aria-labelledby": props.label ? LABEL_ID : undefined, "aria-describedby": getDescribedBy(props), "aria-disabled": props.disabled, "aria-required": props.required, onChange: props.onNativeChange, onFocus: props.open, onBlur: props.close, class: "limel-select__native-control", disabled: props.disabled, multiple: props.multiple }, options));
|
|
100
|
+
return (h("select", { required: props.required, "aria-labelledby": props.label ? LABEL_ID : undefined, "aria-describedby": getDescribedBy(props), "aria-disabled": props.disabled, "aria-required": props.required, "aria-invalid": props.isValid ? undefined : 'true', onChange: props.onNativeChange, onFocus: props.open, onBlur: props.close, class: "limel-select__native-control", disabled: props.disabled, multiple: props.multiple }, options));
|
|
101
101
|
};
|
|
102
102
|
const renderOption = (selectedOption) => {
|
|
103
103
|
return (option) => {
|
|
@@ -270,15 +270,15 @@ export class Slider {
|
|
|
270
270
|
if (this.disabled || this.readonly) {
|
|
271
271
|
inputProps.disabled = true;
|
|
272
272
|
}
|
|
273
|
-
return (h(Host, { key: '6d910879894ffc7ff36df228e4252e4758622ab9', class: this.getContainerClassList() }, h("limel-notched-outline", { key: '56e3b4dbc963b6923c63a33acb21895f4e86c947', labelId: this.inputId, label: this.label, required: this.required, invalid: this.invalid, disabled: this.disabled, readonly: this.readonly, hasValue: !this.isUnset, hasFloatingLabel: true }, h("div", { key: 'f74ef31e0b7d67dcc8a231a05f9b00b84de50b33', slot: "content" }, h("div", { key: '4a4018ac095977f89cba99a0222603469d11ce30', class: "slider", style: { '--slider-fraction': `${fraction}` } }, h("input", Object.assign({ key: '
|
|
273
|
+
return (h(Host, { key: '6d910879894ffc7ff36df228e4252e4758622ab9', class: this.getContainerClassList() }, h("limel-notched-outline", { key: '56e3b4dbc963b6923c63a33acb21895f4e86c947', labelId: this.inputId, label: this.label, required: this.required, invalid: this.invalid, disabled: this.disabled, readonly: this.readonly, hasValue: !this.isUnset, hasFloatingLabel: true }, h("div", { key: 'f74ef31e0b7d67dcc8a231a05f9b00b84de50b33', slot: "content" }, h("div", { key: '4a4018ac095977f89cba99a0222603469d11ce30', class: "slider", style: { '--slider-fraction': `${fraction}` } }, h("input", Object.assign({ key: '3c9dc1fb20dd26ae0ea6cbab0914d5012a9c0184', id: this.inputId, type: "range", min: min, max: max, value: this.displayValue, "aria-describedby": this.helperText
|
|
274
274
|
? this.helperTextId
|
|
275
|
-
: undefined, "aria-valuetext": this.isUnset
|
|
275
|
+
: undefined, "aria-invalid": this.invalid ? 'true' : undefined, "aria-valuetext": this.isUnset
|
|
276
276
|
? translate.get('value-not-set', this.language)
|
|
277
277
|
: undefined, ref: (el) => {
|
|
278
278
|
this.inputElement = el;
|
|
279
|
-
}, onInput: this.handleInput, onChange: this.handleChange, onClick: this.handleClick }, inputProps)), h("div", { key: '
|
|
279
|
+
}, onInput: this.handleInput, onChange: this.handleChange, onClick: this.handleClick }, inputProps)), h("div", { key: '4873567abad9bd78cd44cf244bb5c41e87c10e12', class: "track" }, h("div", { key: '165858a4a1ee9ff35ed17b3f31c358b77143af47', class: "active" }), this.renderStepDots(min, max)), h("div", { key: 'ad6a62247047caad0332385713acf0660473589c', class: "thumb" }, h("div", { key: '10a5b6171a86d156de78987caf36c6256b1d3db7', class: "knob" }), h("div", { key: 'dac6f4e48e2345091c58433124afeb29cca52e02', class: "indicator", "aria-hidden": "true" }, this.isUnset
|
|
280
280
|
? '\u{2194}\u{FE0E}'
|
|
281
|
-
: this.displayValue))), h("div", { key: '
|
|
281
|
+
: this.displayValue))), h("div", { key: 'b62ca0a0dc80b5d6c370d477a7cb340c5c1fb79d', class: "range-labels" }, h("span", { key: '4455915693d0c95db8f5a05ac344e8de3c7efe4d', class: "min" }, this.multiplyByFactor(this.valuemin), this.unit), h("span", { key: 'ef141dec4f89ad937e521cb82b94ad3348904e49', class: "max" }, this.multiplyByFactor(this.valuemax), this.unit)))), this.renderClearButton(), this.renderHelperLine()));
|
|
282
282
|
}
|
|
283
283
|
watchValue() {
|
|
284
284
|
this.syncStateFromValue();
|
|
@@ -55,7 +55,7 @@ export class Switch {
|
|
|
55
55
|
return (h("limel-dynamic-label", { value: this.value, "aria-describedby": this.ariaDescribedBy, defaultLabel: { text: this.label, icon: icon }, labels: this.readonlyLabels }));
|
|
56
56
|
};
|
|
57
57
|
this.renderInteractive = () => {
|
|
58
|
-
return (h("div", { class: "switch" }, h("button", { id: this.fieldId, type: "button", role: "switch", "aria-checked": String(this.value), disabled: this.disabled, onClick: this.handleClick, "aria-describedby": this.ariaDescribedBy }, h("span", { class: "handle" })), h("label", { htmlFor: this.fieldId }, this.label)));
|
|
58
|
+
return (h("div", { class: "switch" }, h("button", { id: this.fieldId, type: "button", role: "switch", "aria-checked": String(this.value), disabled: this.disabled, onClick: this.handleClick, "aria-describedby": this.ariaDescribedBy, "aria-invalid": this.invalid ? 'true' : undefined }, h("span", { class: "handle" })), h("label", { htmlFor: this.fieldId }, this.label)));
|
|
59
59
|
};
|
|
60
60
|
this.renderHelperLine = () => {
|
|
61
61
|
if (!this.hasHelperText()) {
|