@limetech/lime-elements 40.12.3 → 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.
Files changed (86) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/cjs/drag-to-reorder-BP5YkPuu.js +3092 -0
  3. package/dist/cjs/lime-elements.cjs.js +1 -1
  4. package/dist/cjs/{limel-scroller.cjs.entry.js → limel-drag-handle_2.cjs.entry.js} +38 -0
  5. package/dist/cjs/limel-file-viewer.cjs.entry.js +64 -55
  6. package/dist/cjs/limel-form.cjs.entry.js +539 -3171
  7. package/dist/cjs/limel-tab-bar.cjs.entry.js +120 -16
  8. package/dist/cjs/limel-tab-panel.cjs.entry.js +7 -1
  9. package/dist/cjs/loader.cjs.js +1 -1
  10. package/dist/collection/components/form/form.css +59 -0
  11. package/dist/collection/components/form/form.js +49 -2
  12. package/dist/collection/components/form/show-field.js +116 -0
  13. package/dist/collection/components/form/templates/array-field-collapsible-item.js +1 -0
  14. package/dist/collection/components/form/templates/array-field-item.js +5 -0
  15. package/dist/collection/components/form/templates/array-field.js +20 -158
  16. package/dist/collection/components/form/templates/field.js +17 -0
  17. package/dist/collection/components/tab-bar/tab-bar.css +68 -22
  18. package/dist/collection/components/tab-bar/tab-bar.js +131 -4
  19. package/dist/collection/components/tab-bar/tabs.js +23 -0
  20. package/dist/collection/components/tab-panel/tab-panel.js +54 -1
  21. package/dist/collection/util/drag-to-reorder.js +116 -0
  22. package/dist/esm/{_baseForOwn-D5j7fHlv.js → _baseForOwn-vKFWmmaS.js} +1 -1
  23. package/dist/esm/{_baseIsEqual-CyF4P8uM.js → _baseIsEqual-B_JRErB0.js} +1 -1
  24. package/dist/esm/{_baseIteratee-CpbczHY1.js → _baseIteratee-CVz7aes3.js} +1 -1
  25. package/dist/esm/{_baseSet-Cw2IE6ZF.js → _baseSet-BUAq8NGm.js} +1 -1
  26. package/dist/esm/{cloneDeep-DAbOtbs8.js → cloneDeep-Bcll-3f8.js} +1 -1
  27. package/dist/esm/drag-to-reorder-BXmXIwCa.js +3086 -0
  28. package/dist/esm/{isEqual-CE83yS8T.js → isEqual-C-pK6AAU.js} +1 -1
  29. package/dist/esm/lime-elements.js +1 -1
  30. package/dist/esm/limel-chip-set.entry.js +2 -2
  31. package/dist/esm/limel-dialog.entry.js +2 -2
  32. package/dist/esm/{limel-scroller.entry.js → limel-drag-handle_2.entry.js} +38 -1
  33. package/dist/esm/limel-file-dropzone_2.entry.js +3 -3
  34. package/dist/esm/limel-file-viewer.entry.js +64 -55
  35. package/dist/esm/limel-form.entry.js +536 -3168
  36. package/dist/esm/limel-prosemirror-adapter.entry.js +3 -3
  37. package/dist/esm/limel-tab-bar.entry.js +117 -13
  38. package/dist/esm/limel-tab-panel.entry.js +7 -1
  39. package/dist/esm/limel-table.entry.js +4 -4
  40. package/dist/esm/loader.js +1 -1
  41. package/dist/lime-elements/lime-elements.esm.js +1 -1
  42. package/dist/lime-elements/{p-35bc5ed2.entry.js → p-13f2afc9.entry.js} +1 -1
  43. package/dist/lime-elements/p-3b6d4934.entry.js +1 -0
  44. package/dist/lime-elements/p-67767276.entry.js +1 -0
  45. package/dist/lime-elements/{p-fb15f608.entry.js → p-6d65ee35.entry.js} +1 -1
  46. package/dist/lime-elements/p-714ab66d.entry.js +16 -0
  47. package/dist/lime-elements/{p-ff4a8b89.entry.js → p-7ae71c13.entry.js} +1 -1
  48. package/dist/lime-elements/p-BAqAt5It.js +1 -0
  49. package/dist/lime-elements/p-Cu9Ji1iJ.js +1 -0
  50. package/dist/lime-elements/{p-BY5NjPj8.js → p-D9Ezd43A.js} +1 -1
  51. package/dist/lime-elements/{p-nVemxylR.js → p-DFu1q54T.js} +1 -1
  52. package/dist/lime-elements/p-DdnBHYb-.js +7 -0
  53. package/dist/lime-elements/{p-B6-3WXAv.js → p-DeIIlp6m.js} +1 -1
  54. package/dist/lime-elements/p-Dq0uNStc.js +1 -0
  55. package/dist/lime-elements/p-b5bb124f.entry.js +1 -0
  56. package/dist/lime-elements/{p-9168793f.entry.js → p-bd58c761.entry.js} +1 -1
  57. package/dist/lime-elements/{p-458c131f.entry.js → p-c7e34502.entry.js} +1 -1
  58. package/dist/lime-elements/p-fb526a05.entry.js +1 -0
  59. package/dist/types/components/form/form.d.ts +15 -0
  60. package/dist/types/components/form/show-field.d.ts +9 -0
  61. package/dist/types/components/form/templates/array-context.d.ts +4 -0
  62. package/dist/types/components/form/templates/array-field-collapsible-item.d.ts +5 -0
  63. package/dist/types/components/form/templates/array-field.d.ts +3 -13
  64. package/dist/types/components/form/templates/field.d.ts +1 -0
  65. package/dist/types/components/form/templates/object-field.d.ts +2 -2
  66. package/dist/types/components/tab-bar/tab-bar.d.ts +25 -0
  67. package/dist/types/components/tab-bar/tab.types.d.ts +18 -0
  68. package/dist/types/components/tab-bar/tabs.d.ts +12 -1
  69. package/dist/types/components/tab-panel/tab-panel.d.ts +18 -1
  70. package/dist/types/components.d.ts +80 -2
  71. package/dist/types/util/drag-to-reorder.d.ts +65 -0
  72. package/package.json +4 -4
  73. package/dist/cjs/isArrayLikeObject-U2Ddbw4n.js +0 -326
  74. package/dist/cjs/limel-drag-handle.cjs.entry.js +0 -42
  75. package/dist/esm/isArrayLikeObject-CJB8HbtH.js +0 -321
  76. package/dist/esm/limel-drag-handle.entry.js +0 -40
  77. package/dist/lime-elements/p-49eb2f64.entry.js +0 -13
  78. package/dist/lime-elements/p-646731b6.entry.js +0 -1
  79. package/dist/lime-elements/p-8d567e42.entry.js +0 -1
  80. package/dist/lime-elements/p-BQeIv4aA.js +0 -1
  81. package/dist/lime-elements/p-COdirXg1.js +0 -1
  82. package/dist/lime-elements/p-ORUNaKm-.js +0 -1
  83. package/dist/lime-elements/p-a5246373.entry.js +0 -1
  84. package/dist/lime-elements/p-b609552e.entry.js +0 -1
  85. package/dist/lime-elements/p-bf849dc9.entry.js +0 -1
  86. package/dist/lime-elements/p-pk5KIdKH.js +0 -1
@@ -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 Sortable from "sortablejs";
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.teardownDragController();
20
+ this.dragToReorder.teardown();
25
21
  this.container = element !== null && element !== void 0 ? element : undefined;
26
22
  this.setupDragController();
27
23
  };
28
- this.handleSortStart = (event) => {
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
- if (!this.canReorderItems()) {
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: fromPosition, toIndex: toPosition },
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.teardownDragController();
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.teardownDragController();
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
- if (this.sortable) {
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.cachedContextValue = { arraySchema: schema, formData };
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;
@@ -128,12 +128,57 @@ limel-scroller {
128
128
  background-color: rgb(var(--limel-tab-background-color));
129
129
  }
130
130
 
131
- :host(.has-tabs-with-equal-width:not([orientation=vertical])) button {
131
+ :host(.has-tabs-with-equal-width:not([orientation=vertical])) button[role=tab],
132
+ :host(.has-tabs-with-equal-width:not([orientation=vertical])) limel-scroller > div {
132
133
  flex: 1 0 auto;
133
134
  max-width: var(--tab-bar-horizontal-tab-max-width, none);
134
135
  }
135
136
 
136
- button {
137
+ limel-scroller > div:has(limel-drag-handle:hover), limel-scroller > div:has(limel-drag-handle:focus-within), limel-scroller > div.is-elevated, limel-scroller > div.sortable-chosen {
138
+ will-change: transform, box-shadow, border-radius, background-color;
139
+ }
140
+ limel-scroller > div.sortable-ghost {
141
+ border-radius: 0.5rem;
142
+ box-shadow: var(--shadow-depth-64), 0 0.65rem 0.75rem -0.425rem rgb(var(--color-black), 0.3);
143
+ }
144
+ limel-scroller > div.sortable-chosen {
145
+ border-radius: 0.5rem;
146
+ background-color: var(--lime-elevated-surface-background-color);
147
+ }
148
+ limel-scroller > div.is-elevated[draggable=false] {
149
+ animation: sit-here 0.8s forwards;
150
+ }
151
+ @keyframes sit-here {
152
+ 0% {
153
+ border-radius: 0.5rem;
154
+ background-color: var(--lime-elevated-surface-background-color);
155
+ box-shadow: var(--shadow-depth-64), 0 0.65rem 0.75rem -0.425rem rgb(var(--color-black), 0.3);
156
+ }
157
+ 100% {
158
+ box-shadow: none;
159
+ }
160
+ }
161
+ limel-scroller > div {
162
+ position: relative;
163
+ display: flex;
164
+ flex: 0 0 auto;
165
+ }
166
+ limel-scroller > div button[role=tab] {
167
+ flex: 1 1 auto;
168
+ min-width: 0;
169
+ }
170
+ limel-scroller > div span.content {
171
+ padding-right: 2rem;
172
+ }
173
+ limel-scroller > div limel-drag-handle {
174
+ position: absolute;
175
+ z-index: 3;
176
+ top: 0;
177
+ bottom: 0;
178
+ right: 0.25rem;
179
+ }
180
+
181
+ button[role=tab] {
137
182
  appearance: none;
138
183
  background: none;
139
184
  border: none;
@@ -156,34 +201,34 @@ button {
156
201
  white-space: nowrap;
157
202
  font-size: var(--limel-theme-default-font-size);
158
203
  }
159
- button limel-icon {
204
+ button[role=tab] limel-icon {
160
205
  transition: color 0.2s ease;
161
206
  color: rgb(var(--contrast-800));
162
207
  margin-left: -0.25rem;
163
208
  flex-shrink: 0;
164
209
  }
165
- button span.text {
210
+ button[role=tab] span.text {
166
211
  transition: color 0.2s ease;
167
212
  line-height: 1.25;
168
213
  overflow: hidden;
169
214
  text-overflow: ellipsis;
170
215
  color: var(--limel-theme-on-surface-color);
171
216
  }
172
- button:hover span.text {
217
+ button[role=tab]:hover span.text {
173
218
  color: var(--limel-theme-text-primary-on-background-color);
174
219
  }
175
- button limel-badge {
220
+ button[role=tab] limel-badge {
176
221
  margin-right: -0.25rem;
177
222
  box-shadow: 0 0 0 1px rgb(var(--contrast-600));
178
223
  pointer-events: none;
179
224
  flex-shrink: 0;
180
225
  }
181
226
 
182
- button[aria-selected=true] {
227
+ button[role=tab][aria-selected=true] {
183
228
  background-color: var(--limel-active-tab-background-color);
184
229
  z-index: 2;
185
230
  }
186
- button[aria-selected=true]:before, button[aria-selected=true]:after {
231
+ button[role=tab][aria-selected=true]:before, button[role=tab][aria-selected=true]:after {
187
232
  content: "";
188
233
  display: block;
189
234
  width: var(--limel-tab-active-outer-edge-curve-size);
@@ -193,10 +238,10 @@ button[aria-selected=true]:before, button[aria-selected=true]:after {
193
238
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill-rule='evenodd'%20stroke-linejoin='round'%20stroke-miterlimit='2'%20clip-rule='evenodd'%20viewBox='0%200%2050%2050'%3E%3Cdefs/%3E%3Cpath%20d='M0%200c0%2027.594%2022.406%2050%2050%2050H0V0z'/%3E%3C/svg%3E");
194
239
  mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill-rule='evenodd'%20stroke-linejoin='round'%20stroke-miterlimit='2'%20clip-rule='evenodd'%20viewBox='0%200%2050%2050'%3E%3Cdefs/%3E%3Cpath%20d='M0%200c0%2027.594%2022.406%2050%2050%2050H0V0z'/%3E%3C/svg%3E");
195
240
  }
196
- button[aria-selected=true] limel-icon {
241
+ button[role=tab][aria-selected=true] limel-icon {
197
242
  color: var(--limel-active-tab-color);
198
243
  }
199
- button[aria-selected=true] span.text {
244
+ button[role=tab][aria-selected=true] span.text {
200
245
  color: var(--limel-active-tab-color);
201
246
  }
202
247
 
@@ -211,15 +256,15 @@ span.content {
211
256
  padding: 0 0.75rem;
212
257
  border-radius: calc(var(--limel-tab-border-radius) - var(--limel-tab-content-inset));
213
258
  }
214
- button:focus-visible span.content {
259
+ button[role=tab]:focus-visible span.content {
215
260
  transition-duration: 0.3s;
216
261
  box-shadow: var(--shadow-depth-8-focused);
217
262
  }
218
263
 
219
- :host(limel-tab-bar:not([orientation=vertical])) button {
264
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab] {
220
265
  max-width: var(--tab-bar-horizontal-tab-max-width, 16rem);
221
266
  }
222
- :host(limel-tab-bar:not([orientation=vertical])) button:not([aria-selected=true]):after {
267
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab]:not([aria-selected=true]):after {
223
268
  content: "";
224
269
  display: block;
225
270
  background-color: var(--limel-tab-separator-background-color);
@@ -232,20 +277,21 @@ button:focus-visible span.content {
232
277
  border-radius: 1rem;
233
278
  right: calc(-1 * var(--limel-tab-separator-width));
234
279
  }
235
- :host(limel-tab-bar:not([orientation=vertical])) button:not([aria-selected=true]):last-of-type:after {
280
+ :host(limel-tab-bar:not([orientation=vertical])) limel-scroller > :last-child:not([aria-selected=true]):after,
281
+ :host(limel-tab-bar:not([orientation=vertical])) limel-scroller > :last-child > button[role=tab]:not([aria-selected=true]):after {
236
282
  display: none;
237
283
  }
238
- :host(limel-tab-bar:not([orientation=vertical])) button[aria-selected=true] {
284
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab][aria-selected=true] {
239
285
  border-radius: var(--limel-tab-border-radius) var(--limel-tab-border-radius) 0 0;
240
286
  }
241
- :host(limel-tab-bar:not([orientation=vertical])) button[aria-selected=true]:before, :host(limel-tab-bar:not([orientation=vertical])) button[aria-selected=true]:after {
287
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab][aria-selected=true]:before, :host(limel-tab-bar:not([orientation=vertical])) button[role=tab][aria-selected=true]:after {
242
288
  bottom: 0;
243
289
  }
244
- :host(limel-tab-bar:not([orientation=vertical])) button[aria-selected=true]:before {
290
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab][aria-selected=true]:before {
245
291
  left: calc(-1 * var(--limel-tab-active-outer-edge-curve-size));
246
292
  transform: rotateY(180deg);
247
293
  }
248
- :host(limel-tab-bar:not([orientation=vertical])) button[aria-selected=true]:after {
294
+ :host(limel-tab-bar:not([orientation=vertical])) button[role=tab][aria-selected=true]:after {
249
295
  right: calc(-1 * var(--limel-tab-active-outer-edge-curve-size));
250
296
  }
251
297
 
@@ -262,17 +308,17 @@ button:focus-visible span.content {
262
308
  0 var(--limel-tab-active-outer-edge-curve-size) 0.25rem;
263
309
  height: 100%;
264
310
  }
265
- :host(limel-tab-bar[orientation=vertical]) button[aria-selected=true] {
311
+ :host(limel-tab-bar[orientation=vertical]) button[role=tab][aria-selected=true] {
266
312
  border-radius: var(--limel-tab-border-radius) 0 0 var(--limel-tab-border-radius);
267
313
  }
268
- :host(limel-tab-bar[orientation=vertical]) button[aria-selected=true]:before, :host(limel-tab-bar[orientation=vertical]) button[aria-selected=true]:after {
314
+ :host(limel-tab-bar[orientation=vertical]) button[role=tab][aria-selected=true]:before, :host(limel-tab-bar[orientation=vertical]) button[role=tab][aria-selected=true]:after {
269
315
  right: 0;
270
316
  }
271
- :host(limel-tab-bar[orientation=vertical]) button[aria-selected=true]:before {
317
+ :host(limel-tab-bar[orientation=vertical]) button[role=tab][aria-selected=true]:before {
272
318
  bottom: 100%;
273
319
  transform: rotateY(180deg);
274
320
  }
275
- :host(limel-tab-bar[orientation=vertical]) button[aria-selected=true]:after {
321
+ :host(limel-tab-bar[orientation=vertical]) button[role=tab][aria-selected=true]:after {
276
322
  top: 100%;
277
323
  transform: rotate(180deg);
278
324
  }