@limetech/lime-elements 39.17.0 → 39.17.2

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 CHANGED
@@ -1,3 +1,19 @@
1
+ ## [39.17.2](https://github.com/Lundalogik/lime-elements/compare/v39.17.1...v39.17.2) (2026-04-27)
2
+
3
+ ### Bug Fixes
4
+
5
+
6
+ * **card:** don't show hover effects when card is neither clickable nor has 3D effect ([20cf51b](https://github.com/Lundalogik/lime-elements/commit/20cf51b5a425a05eb259d39f80c24ca9bf130fe7))
7
+
8
+ ## [39.17.1](https://github.com/Lundalogik/lime-elements/compare/v39.17.0...v39.17.1) (2026-04-27)
9
+
10
+ ### Bug Fixes
11
+
12
+
13
+ * **form:** allow clearing custom components nested inside array items ([02910c1](https://github.com/Lundalogik/lime-elements/commit/02910c1b10fca4e5fbea564555dc4db75be82c99))
14
+ * **form:** preserve nested-object leaf data inside array items ([8e29997](https://github.com/Lundalogik/lime-elements/commit/8e299974ed9b3b14dd9eda4313ded2106716225d))
15
+ * **form:** reset obsolete dependent fields inside array items on trigger change ([7e1458e](https://github.com/Lundalogik/lime-elements/commit/7e1458e54467db9f2019a1160a52dad94b7e7a14))
16
+
1
17
  ## [39.17.0](https://github.com/Lundalogik/lime-elements/compare/v39.16.4...v39.17.0) (2026-04-27)
2
18
 
3
19
  ### Features
@@ -5,7 +5,7 @@ var isItem = require('./is-item-DMELyaao.js');
5
5
  var getIconProps = require('./get-icon-props-CwpDdQDI.js');
6
6
  var _3dTiltHoverEffect = require('./3d-tilt-hover-effect-DI8kt0Y0.js');
7
7
 
8
- const cardCss = () => `@charset "UTF-8";*{box-sizing:border-box;min-width:0;min-height:0}:host(limel-card){display:flex;border-radius:var(--card-border-radius, 0.95rem)}section{box-sizing:border-box;display:flex;gap:0.5rem;flex-direction:column}:host(limel-card[orientation=landscape]) section{flex-direction:row}section{width:100%;border-radius:var(--card-border-radius, 0.95rem);border:1px solid rgb(var(--contrast-500));padding:0.25rem;background-color:var(--card-background-color, rgb(var(--contrast-300)))}section:hover{border-color:transparent;background-color:var(--card-background-color--hovered, var(--card-background-color, rgb(var(--contrast-200))))}.body{flex-grow:1;display:flex;flex-direction:column;gap:0.5rem}.image-wrapper{flex-shrink:0;display:flex;align-items:center;justify-content:center}:host(limel-card[orientation=portrait]) .image-wrapper{width:100%}:host(limel-card[orientation=landscape]) .image-wrapper{flex-shrink:0;max-width:40%;height:100%}:host(limel-card[orientation=landscape]) .image-wrapper img{height:100%}img{transition:filter 0.6s ease;object-fit:cover;border-radius:calc(var(--card-border-radius, 0.95rem) / 1.4);min-width:0.5rem;min-height:0.5rem;max-width:100%;max-height:100%}section:hover img,section:focus-visible img{transition-duration:0.2s;filter:saturate(1.3)}.markdown-wrapper{position:relative;flex-grow:1;display:flex;flex-direction:column}.markdown-wrapper limel-markdown{overflow-y:auto;flex-grow:1;padding:0.5rem 0.75rem}.markdown-wrapper::before{top:0;background:radial-gradient(farthest-side at 50% 0%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0))}.markdown-wrapper::after{bottom:0;background:radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0))}.markdown-wrapper::before,.markdown-wrapper::after{content:"";pointer-events:none;position:absolute;right:0;left:0;height:0.75rem;opacity:0;transition:opacity 0.6s ease;background-repeat:no-repeat}.markdown-wrapper.can-scroll-up::before,.markdown-wrapper.can-scroll-down::after{opacity:1}header{flex-shrink:0;display:flex;justify-content:center;gap:0.5rem;padding:0.25rem 0.75rem}:host(limel-card[orientation=landscape]) header{padding-top:0.5rem}header:has(limel-icon){padding-left:0.25rem}header .headings{flex-grow:1;display:flex;flex-direction:column;gap:0.125rem}header .title{padding-right:1.25rem}header limel-icon{flex-shrink:0;width:2rem}header h1{font-size:1.125rem;font-weight:500;color:var(--card-heading-color, rgb(var(--contrast-1100)));letter-spacing:-0.03125rem}header h2{font-size:var(--limel-theme-default-font-size);font-weight:400;color:var(--card-subheading-color, rgb(var(--contrast-1000)))}header h1,header h2{word-break:break-word;hyphens:auto;-webkit-hyphens:auto;margin:0}limel-action-bar{flex-shrink:0;--action-bar-background-color:transparent;margin-left:auto}limel-3d-hover-effect-glow{border-radius:var(--card-border-radius, 0.95rem)}:host(limel-card[show-3d-effect]){isolation:isolate;transform-style:preserve-3d;perspective:1000px}@media (prefers-reduced-motion){:host(limel-card[show-3d-effect]){perspective:2000px}}section{position:relative;transition-duration:0.8s;transition-property:transform, box-shadow, background-color;transition-timing-function:ease-out;transform:scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg)}section:focus{outline:none}section:hover,section:focus,section:focus-visible,section:focus-within{will-change:background-color, box-shadow, transform}section:hover,section:focus,section:focus-visible,section:active{transition-duration:0.2s}section:hover,section:focus-visible{box-shadow:var(--button-shadow-hovered), var(--shadow-depth-16)}section:hover{transform:scale3d(1.01, 1.01, 1.01) rotate3d(var(--limel-3d-hover-effect-rotate3d))}section:focus-visible{outline:none;transform:scale3d(1.01, 1.01, 1.01)}section:hover limel-3d-hover-effect-glow{--limel-3d-hover-effect-glow-opacity:0.5}@media (prefers-reduced-motion){section:hover limel-3d-hover-effect-glow{--limel-3d-hover-effect-glow-opacity:0.2}}:host(limel-card[clickable]) section{cursor:pointer;box-shadow:var(--button-shadow-normal)}:host(limel-card[clickable]) section:hover,:host(limel-card[clickable]) section:focus-visible{box-shadow:var(--button-shadow-hovered), var(--shadow-depth-16)}:host(limel-card[clickable]) section:active{transform:scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);box-shadow:var(--button-shadow-pressed)}:host(limel-card[clickable]) section:focus-visible{box-shadow:var(--shadow-depth-8-focused), var(--button-shadow-hovered)}:host(limel-card[clickable]) section:focus-visible:active{box-shadow:var(--shadow-depth-8-focused), var(--button-shadow-pressed)}:host(limel-card[selected]) section,:host(limel-card[selected]) section:hover,:host(limel-card[selected]) section:focus-visible,:host(limel-card[selected]) section:active{box-shadow:var(--shadow-focused-state)}`;
8
+ const cardCss = () => `@charset "UTF-8";*{box-sizing:border-box;min-width:0;min-height:0}:host(limel-card){display:flex;border-radius:var(--card-border-radius, 0.95rem)}section{box-sizing:border-box;display:flex;gap:0.5rem;flex-direction:column}:host(limel-card[orientation=landscape]) section{flex-direction:row}section{width:100%;border-radius:var(--card-border-radius, 0.95rem);border:1px solid rgb(var(--contrast-500));padding:0.25rem;background-color:var(--card-background-color, rgb(var(--contrast-300)))}:host(limel-card[clickable]) section,:host(limel-card[show-3d-effect]) section{position:relative;transition-duration:0.8s;transition-property:transform, box-shadow, background-color;transition-timing-function:ease-out;transform:scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg)}:host(limel-card[clickable]) section:focus,:host(limel-card[show-3d-effect]) section:focus{outline:none}:host(limel-card[clickable]) section:hover,:host(limel-card[clickable]) section:focus,:host(limel-card[clickable]) section:focus-visible,:host(limel-card[clickable]) section:focus-within,:host(limel-card[show-3d-effect]) section:hover,:host(limel-card[show-3d-effect]) section:focus,:host(limel-card[show-3d-effect]) section:focus-visible,:host(limel-card[show-3d-effect]) section:focus-within{will-change:background-color, box-shadow, transform}:host(limel-card[clickable]) section:hover,:host(limel-card[clickable]) section:focus,:host(limel-card[clickable]) section:focus-visible,:host(limel-card[clickable]) section:active,:host(limel-card[show-3d-effect]) section:hover,:host(limel-card[show-3d-effect]) section:focus,:host(limel-card[show-3d-effect]) section:focus-visible,:host(limel-card[show-3d-effect]) section:active{transition-duration:0.2s}:host(limel-card[clickable]) section:hover,:host(limel-card[clickable]) section:focus-visible,:host(limel-card[show-3d-effect]) section:hover,:host(limel-card[show-3d-effect]) section:focus-visible{box-shadow:var(--button-shadow-hovered), var(--shadow-depth-16)}:host(limel-card[clickable]) section:hover,:host(limel-card[show-3d-effect]) section:hover{transform:scale3d(1.01, 1.01, 1.01) rotate3d(var(--limel-3d-hover-effect-rotate3d))}:host(limel-card[clickable]) section:focus-visible,:host(limel-card[show-3d-effect]) section:focus-visible{outline:none;transform:scale3d(1.01, 1.01, 1.01)}:host(limel-card[clickable]) section:hover limel-3d-hover-effect-glow,:host(limel-card[show-3d-effect]) section:hover limel-3d-hover-effect-glow{--limel-3d-hover-effect-glow-opacity:0.5}@media (prefers-reduced-motion){:host(limel-card[clickable]) section:hover limel-3d-hover-effect-glow,:host(limel-card[show-3d-effect]) section:hover limel-3d-hover-effect-glow{--limel-3d-hover-effect-glow-opacity:0.2}}:host(limel-card[clickable]) section{cursor:pointer;box-shadow:var(--button-shadow-normal)}:host(limel-card[clickable]) section:hover,:host(limel-card[clickable]) section:focus-visible{box-shadow:var(--button-shadow-hovered), var(--shadow-depth-16)}:host(limel-card[clickable]) section:active{transform:scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);box-shadow:var(--button-shadow-pressed)}:host(limel-card[clickable]) section:focus-visible{box-shadow:var(--shadow-depth-8-focused), var(--button-shadow-hovered)}:host(limel-card[clickable]) section:focus-visible:active{box-shadow:var(--shadow-depth-8-focused), var(--button-shadow-pressed)}:host(limel-card[clickable]) section:hover{border-color:transparent;background-color:var(--card-background-color--hovered, var(--card-background-color, rgb(var(--contrast-200))))}.body{flex-grow:1;display:flex;flex-direction:column;gap:0.5rem}.image-wrapper{flex-shrink:0;display:flex;align-items:center;justify-content:center}:host(limel-card[orientation=portrait]) .image-wrapper{width:100%}:host(limel-card[orientation=landscape]) .image-wrapper{flex-shrink:0;max-width:40%;height:100%}:host(limel-card[orientation=landscape]) .image-wrapper img{height:100%}img{transition:filter 0.6s ease;object-fit:cover;border-radius:calc(var(--card-border-radius, 0.95rem) / 1.4);min-width:0.5rem;min-height:0.5rem;max-width:100%;max-height:100%}:host(limel-card[show-3d-effect]) section:hover img,:host(limel-card[show-3d-effect]) section:focus-visible img{transition-duration:0.2s;filter:saturate(1.3)}.markdown-wrapper{position:relative;flex-grow:1;display:flex;flex-direction:column}.markdown-wrapper limel-markdown{overflow-y:auto;flex-grow:1;padding:0.5rem 0.75rem}.markdown-wrapper::before{top:0;background:radial-gradient(farthest-side at 50% 0%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0))}.markdown-wrapper::after{bottom:0;background:radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0))}.markdown-wrapper::before,.markdown-wrapper::after{content:"";pointer-events:none;position:absolute;right:0;left:0;height:0.75rem;opacity:0;transition:opacity 0.6s ease;background-repeat:no-repeat}.markdown-wrapper.can-scroll-up::before,.markdown-wrapper.can-scroll-down::after{opacity:1}header{flex-shrink:0;display:flex;justify-content:center;gap:0.5rem;padding:0.25rem 0.75rem}:host(limel-card[orientation=landscape]) header{padding-top:0.5rem}header:has(limel-icon){padding-left:0.25rem}header .headings{flex-grow:1;display:flex;flex-direction:column;gap:0.125rem}header .title{padding-right:1.25rem}header limel-icon{flex-shrink:0;width:2rem}header h1{font-size:1.125rem;font-weight:500;color:var(--card-heading-color, rgb(var(--contrast-1100)));letter-spacing:-0.03125rem}header h2{font-size:var(--limel-theme-default-font-size);font-weight:400;color:var(--card-subheading-color, rgb(var(--contrast-1000)))}header h1,header h2{word-break:break-word;hyphens:auto;-webkit-hyphens:auto;margin:0}limel-action-bar{flex-shrink:0;--action-bar-background-color:transparent;margin-left:auto}limel-3d-hover-effect-glow{border-radius:var(--card-border-radius, 0.95rem)}:host(limel-card[show-3d-effect]){isolation:isolate;transform-style:preserve-3d;perspective:1000px}@media (prefers-reduced-motion){:host(limel-card[show-3d-effect]){perspective:2000px}}:host(limel-card[selected]) section,:host(limel-card[selected]) section:hover,:host(limel-card[selected]) section:focus-visible,:host(limel-card[selected]) section:active{box-shadow:var(--shadow-focused-state)}`;
9
9
 
10
10
  const Card = class {
11
11
  constructor(hostRef) {
@@ -39813,6 +39813,21 @@ function isCustomObjectSchema(schema) {
39813
39813
  function isAdditionalProperty(schema) {
39814
39814
  return schema[ADDITIONAL_PROPERTY_FLAG] === true;
39815
39815
  }
39816
+ /**
39817
+ * Check whether a schema permits `null` as a valid value. Used to decide
39818
+ * whether a cleared field value should be preserved as `null` or converted
39819
+ * to `undefined` so the property can be removed from the form data.
39820
+ *
39821
+ * @param schema - the schema to check
39822
+ * @returns true if the schema's type is `'null'` or includes `'null'`
39823
+ */
39824
+ const schemaAllowsNull = (schema) => {
39825
+ const type = schema === null || schema === void 0 ? void 0 : schema.type;
39826
+ if (!type) {
39827
+ return false;
39828
+ }
39829
+ return Array.isArray(type) ? type.includes('null') : type === 'null';
39830
+ };
39816
39831
 
39817
39832
  /**
39818
39833
  * A widget is a concept in react-jsonschema-form (rjsf).
@@ -40007,7 +40022,7 @@ const BaseSchemaField = defaultFields$2.SchemaField;
40007
40022
  * @returns whether or not null should be changed to undefined
40008
40023
  */
40009
40024
  const shouldChangeToUndefined = (value, schema) => {
40010
- return value === null && !schema.type.includes('null');
40025
+ return value === null && !schemaAllowsNull(schema);
40011
40026
  };
40012
40027
  const hasCustomComponent = (schema) => {
40013
40028
  var _a, _b;
@@ -40094,9 +40109,26 @@ class SchemaField extends React.Component {
40094
40109
  const { formData, schema } = this.props;
40095
40110
  const { rootSchema } = this.props.registry;
40096
40111
  this.setState({ modified: true });
40112
+ if (this.isDeepLeafChange(path)) {
40113
+ this.props.onChange(data, path);
40114
+ return;
40115
+ }
40097
40116
  const newData = resetDependentFields(formData, data, schema, rootSchema);
40098
40117
  this.props.onChange(newData, path);
40099
40118
  }
40119
+ /**
40120
+ * RJSF v6's built-in ArrayField shares one onChange handler across all
40121
+ * descendants, so changes to leaves deep inside an array item bubble
40122
+ * through this SchemaField with a path that is deeper than its own.
40123
+ * The enclosing ArrayField rebuilds the affected item and runs
40124
+ * `resetDependentFields` at the item level (see array-field.ts), so we
40125
+ * must pass the partial leaf data through untouched here.
40126
+ * @param path
40127
+ */
40128
+ isDeepLeafChange(path) {
40129
+ const { fieldPathId } = this.props;
40130
+ return !!fieldPathId && path.length > fieldPathId.path.length;
40131
+ }
40100
40132
  buildCustomComponentProps() {
40101
40133
  const { disabled, readonly, name, registry, schema: rawSchema, errorSchema, fieldPathId, } = this.props;
40102
40134
  const schema = rawSchema;
@@ -40208,6 +40240,10 @@ class ArrayField extends React.Component {
40208
40240
  handleChange(newData, path) {
40209
40241
  const { formData: oldData, schema } = this.props;
40210
40242
  const { rootSchema } = this.props.registry;
40243
+ if (this.isDeepLeafChange(path)) {
40244
+ this.handleDeepLeafChange(newData, path);
40245
+ return;
40246
+ }
40211
40247
  // This case handles when the first list item is added. When there are no
40212
40248
  // items we get undefined instead of []
40213
40249
  if (!oldData) {
@@ -40237,11 +40273,102 @@ class ArrayField extends React.Component {
40237
40273
  }
40238
40274
  this.props.onChange(newData, path);
40239
40275
  }
40276
+ /**
40277
+ * RJSF v6's built-in ArrayField shares one onChange handler across all
40278
+ * descendants, so changes to leaves deep inside an array item bubble
40279
+ * through here with the leaf value (not the full array) and a path that
40280
+ * is deeper than this field's own path.
40281
+ * @param path
40282
+ */
40283
+ isDeepLeafChange(path) {
40284
+ const { formData, fieldPathId } = this.props;
40285
+ return (Array.isArray(formData) &&
40286
+ !!fieldPathId &&
40287
+ path.length > fieldPathId.path.length);
40288
+ }
40289
+ /**
40290
+ * Rebuild the affected item locally so we can (a) restore `undefined`
40291
+ * when the leaf schema does not allow `null` — the built-in handler
40292
+ * coerces `undefined` to `null`, which breaks field clearing for custom
40293
+ * components — and (b) still run `resetDependentFields` at the item
40294
+ * level so sibling fields that became obsolete for the new data are
40295
+ * cleared.
40296
+ * @param newData
40297
+ * @param path
40298
+ */
40299
+ handleDeepLeafChange(newData, path) {
40300
+ const { formData: oldData, fieldPathId } = this.props;
40301
+ const [indexSegment, ...pathInItem] = path.slice(fieldPathId.path.length);
40302
+ const index = Number(indexSegment);
40303
+ const newArray = [...oldData];
40304
+ newArray[index] = this.buildResetItem(newData, index, pathInItem);
40305
+ this.props.onChange(newArray, fieldPathId.path);
40306
+ }
40307
+ buildResetItem(newData, index, pathInItem) {
40308
+ const { formData: oldData, schema } = this.props;
40309
+ const { rootSchema } = this.props.registry;
40310
+ const itemSchema = (Array.isArray(schema.items) ? schema.items[index] : schema.items);
40311
+ const leafSchema = getSchemaAtPath(itemSchema, pathInItem);
40312
+ const value = newData === null && !schemaAllowsNull(leafSchema)
40313
+ ? undefined
40314
+ : newData;
40315
+ const oldItem = oldData[index];
40316
+ const newItem = setValueAtPath(oldItem, pathInItem, value);
40317
+ return resetDependentFields(oldItem, newItem, itemSchema, rootSchema);
40318
+ }
40240
40319
  render() {
40241
40320
  const arrayProps = Object.assign(Object.assign({}, this.props), { onChange: this.handleChange });
40242
40321
  return React.createElement('div', { ref: this.setWrapper }, React.createElement(BaseArrayField, arrayProps));
40243
40322
  }
40244
40323
  }
40324
+ function getSchemaAtPath(schema, pathSegments) {
40325
+ let current = schema;
40326
+ for (const segment of pathSegments) {
40327
+ if (!current) {
40328
+ return;
40329
+ }
40330
+ current = resolveSchemaSegment(current, segment);
40331
+ }
40332
+ return current;
40333
+ }
40334
+ function resolveSchemaSegment(schema, segment) {
40335
+ var _a;
40336
+ if (typeof segment === 'number') {
40337
+ const items = schema.items;
40338
+ return Array.isArray(items) ? items[segment] : items;
40339
+ }
40340
+ return (_a = schema.properties) === null || _a === void 0 ? void 0 : _a[segment];
40341
+ }
40342
+ /**
40343
+ * Return a copy of `target` with `value` written at the location described
40344
+ * by `pathSegments`, creating intermediate containers as needed. Numeric
40345
+ * segments address arrays; string segments address object keys. `target`
40346
+ * is not mutated.
40347
+ *
40348
+ * @param target
40349
+ * @param pathSegments
40350
+ * @param value
40351
+ * @example
40352
+ * setValueAtPath({ a: { b: 1 } }, ['a', 'b'], 2); // → { a: { b: 2 } }
40353
+ * setValueAtPath([{ x: 1 }], [0, 'x'], 9); // → [{ x: 9 }]
40354
+ */
40355
+ function setValueAtPath(target, pathSegments, value) {
40356
+ if (pathSegments.length === 0) {
40357
+ return value;
40358
+ }
40359
+ const clone = cloneContainer(target, pathSegments[0]);
40360
+ set(clone, pathSegments, value);
40361
+ return clone;
40362
+ }
40363
+ function cloneContainer(target, firstSegment) {
40364
+ if (Array.isArray(target) || isPlainObject(target)) {
40365
+ return cloneDeep.cloneDeep(target);
40366
+ }
40367
+ if (typeof firstSegment === 'number') {
40368
+ return [];
40369
+ }
40370
+ return {};
40371
+ }
40245
40372
 
40246
40373
  class CodeEditor extends React.Component {
40247
40374
  constructor(props) {
@@ -131,7 +131,58 @@ section {
131
131
  padding: 0.25rem;
132
132
  background-color: var(--card-background-color, rgb(var(--contrast-300)));
133
133
  }
134
- section:hover {
134
+ :host(limel-card[clickable]) section, :host(limel-card[show-3d-effect]) section {
135
+ position: relative;
136
+ transition-duration: 0.8s;
137
+ transition-property: transform, box-shadow, background-color;
138
+ transition-timing-function: ease-out;
139
+ transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);
140
+ }
141
+ :host(limel-card[clickable]) section:focus, :host(limel-card[show-3d-effect]) section:focus {
142
+ outline: none;
143
+ }
144
+ :host(limel-card[clickable]) section:hover, :host(limel-card[clickable]) section:focus, :host(limel-card[clickable]) section:focus-visible, :host(limel-card[clickable]) section:focus-within, :host(limel-card[show-3d-effect]) section:hover, :host(limel-card[show-3d-effect]) section:focus, :host(limel-card[show-3d-effect]) section:focus-visible, :host(limel-card[show-3d-effect]) section:focus-within {
145
+ will-change: background-color, box-shadow, transform;
146
+ }
147
+ :host(limel-card[clickable]) section:hover, :host(limel-card[clickable]) section:focus, :host(limel-card[clickable]) section:focus-visible, :host(limel-card[clickable]) section:active, :host(limel-card[show-3d-effect]) section:hover, :host(limel-card[show-3d-effect]) section:focus, :host(limel-card[show-3d-effect]) section:focus-visible, :host(limel-card[show-3d-effect]) section:active {
148
+ transition-duration: 0.2s;
149
+ }
150
+ :host(limel-card[clickable]) section:hover, :host(limel-card[clickable]) section:focus-visible, :host(limel-card[show-3d-effect]) section:hover, :host(limel-card[show-3d-effect]) section:focus-visible {
151
+ box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
152
+ }
153
+ :host(limel-card[clickable]) section:hover, :host(limel-card[show-3d-effect]) section:hover {
154
+ transform: scale3d(1.01, 1.01, 1.01) rotate3d(var(--limel-3d-hover-effect-rotate3d));
155
+ }
156
+ :host(limel-card[clickable]) section:focus-visible, :host(limel-card[show-3d-effect]) section:focus-visible {
157
+ outline: none;
158
+ transform: scale3d(1.01, 1.01, 1.01);
159
+ }
160
+ :host(limel-card[clickable]) section:hover limel-3d-hover-effect-glow, :host(limel-card[show-3d-effect]) section:hover limel-3d-hover-effect-glow {
161
+ --limel-3d-hover-effect-glow-opacity: 0.5;
162
+ }
163
+ @media (prefers-reduced-motion) {
164
+ :host(limel-card[clickable]) section:hover limel-3d-hover-effect-glow, :host(limel-card[show-3d-effect]) section:hover limel-3d-hover-effect-glow {
165
+ --limel-3d-hover-effect-glow-opacity: 0.2;
166
+ }
167
+ }
168
+ :host(limel-card[clickable]) section {
169
+ cursor: pointer;
170
+ box-shadow: var(--button-shadow-normal);
171
+ }
172
+ :host(limel-card[clickable]) section:hover, :host(limel-card[clickable]) section:focus-visible {
173
+ box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
174
+ }
175
+ :host(limel-card[clickable]) section:active {
176
+ transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);
177
+ box-shadow: var(--button-shadow-pressed);
178
+ }
179
+ :host(limel-card[clickable]) section:focus-visible {
180
+ box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-hovered);
181
+ }
182
+ :host(limel-card[clickable]) section:focus-visible:active {
183
+ box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-pressed);
184
+ }
185
+ :host(limel-card[clickable]) section:hover {
135
186
  border-color: transparent;
136
187
  background-color: var(--card-background-color--hovered, var(--card-background-color, rgb(var(--contrast-200))));
137
188
  }
@@ -170,7 +221,7 @@ img {
170
221
  max-width: 100%;
171
222
  max-height: 100%;
172
223
  }
173
- section:hover img, section:focus-visible img {
224
+ :host(limel-card[show-3d-effect]) section:hover img, :host(limel-card[show-3d-effect]) section:focus-visible img {
174
225
  transition-duration: 0.2s;
175
226
  filter: saturate(1.3);
176
227
  }
@@ -275,58 +326,6 @@ limel-3d-hover-effect-glow {
275
326
  }
276
327
  }
277
328
 
278
- section {
279
- position: relative;
280
- transition-duration: 0.8s;
281
- transition-property: transform, box-shadow, background-color;
282
- transition-timing-function: ease-out;
283
- transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);
284
- }
285
- section:focus {
286
- outline: none;
287
- }
288
- section:hover, section:focus, section:focus-visible, section:focus-within {
289
- will-change: background-color, box-shadow, transform;
290
- }
291
- section:hover, section:focus, section:focus-visible, section:active {
292
- transition-duration: 0.2s;
293
- }
294
- section:hover, section:focus-visible {
295
- box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
296
- }
297
- section:hover {
298
- transform: scale3d(1.01, 1.01, 1.01) rotate3d(var(--limel-3d-hover-effect-rotate3d));
299
- }
300
- section:focus-visible {
301
- outline: none;
302
- transform: scale3d(1.01, 1.01, 1.01);
303
- }
304
- section:hover limel-3d-hover-effect-glow {
305
- --limel-3d-hover-effect-glow-opacity: 0.5;
306
- }
307
- @media (prefers-reduced-motion) {
308
- section:hover limel-3d-hover-effect-glow {
309
- --limel-3d-hover-effect-glow-opacity: 0.2;
310
- }
311
- }
312
- :host(limel-card[clickable]) section {
313
- cursor: pointer;
314
- box-shadow: var(--button-shadow-normal);
315
- }
316
- :host(limel-card[clickable]) section:hover, :host(limel-card[clickable]) section:focus-visible {
317
- box-shadow: var(--button-shadow-hovered), var(--shadow-depth-16);
318
- }
319
- :host(limel-card[clickable]) section:active {
320
- transform: scale3d(1, 1, 1) rotate3d(0, 0, 0, 0deg);
321
- box-shadow: var(--button-shadow-pressed);
322
- }
323
- :host(limel-card[clickable]) section:focus-visible {
324
- box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-hovered);
325
- }
326
- :host(limel-card[clickable]) section:focus-visible:active {
327
- box-shadow: var(--shadow-depth-8-focused), var(--button-shadow-pressed);
328
- }
329
-
330
329
  :host(limel-card[selected]) section, :host(limel-card[selected]) section:hover, :host(limel-card[selected]) section:focus-visible, :host(limel-card[selected]) section:active {
331
330
  box-shadow: var(--shadow-focused-state);
332
331
  }
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { getDefaultRegistry } from "@rjsf/core";
3
- import { resetDependentFields } from "./field-helpers";
3
+ import { cloneDeep, isPlainObject, set } from "lodash-es";
4
+ import { resetDependentFields, schemaAllowsNull } from "./field-helpers";
4
5
  import { ARRAY_REORDER_EVENT } from "../templates/array-field";
5
6
  const { fields: defaultFields } = getDefaultRegistry();
6
7
  const BaseArrayField = defaultFields.ArrayField;
@@ -69,6 +70,10 @@ export class ArrayField extends React.Component {
69
70
  handleChange(newData, path) {
70
71
  const { formData: oldData, schema } = this.props;
71
72
  const { rootSchema } = this.props.registry;
73
+ if (this.isDeepLeafChange(path)) {
74
+ this.handleDeepLeafChange(newData, path);
75
+ return;
76
+ }
72
77
  // This case handles when the first list item is added. When there are no
73
78
  // items we get undefined instead of []
74
79
  if (!oldData) {
@@ -98,8 +103,99 @@ export class ArrayField extends React.Component {
98
103
  }
99
104
  this.props.onChange(newData, path);
100
105
  }
106
+ /**
107
+ * RJSF v6's built-in ArrayField shares one onChange handler across all
108
+ * descendants, so changes to leaves deep inside an array item bubble
109
+ * through here with the leaf value (not the full array) and a path that
110
+ * is deeper than this field's own path.
111
+ * @param path
112
+ */
113
+ isDeepLeafChange(path) {
114
+ const { formData, fieldPathId } = this.props;
115
+ return (Array.isArray(formData) &&
116
+ !!fieldPathId &&
117
+ path.length > fieldPathId.path.length);
118
+ }
119
+ /**
120
+ * Rebuild the affected item locally so we can (a) restore `undefined`
121
+ * when the leaf schema does not allow `null` — the built-in handler
122
+ * coerces `undefined` to `null`, which breaks field clearing for custom
123
+ * components — and (b) still run `resetDependentFields` at the item
124
+ * level so sibling fields that became obsolete for the new data are
125
+ * cleared.
126
+ * @param newData
127
+ * @param path
128
+ */
129
+ handleDeepLeafChange(newData, path) {
130
+ const { formData: oldData, fieldPathId } = this.props;
131
+ const [indexSegment, ...pathInItem] = path.slice(fieldPathId.path.length);
132
+ const index = Number(indexSegment);
133
+ const newArray = [...oldData];
134
+ newArray[index] = this.buildResetItem(newData, index, pathInItem);
135
+ this.props.onChange(newArray, fieldPathId.path);
136
+ }
137
+ buildResetItem(newData, index, pathInItem) {
138
+ const { formData: oldData, schema } = this.props;
139
+ const { rootSchema } = this.props.registry;
140
+ const itemSchema = (Array.isArray(schema.items) ? schema.items[index] : schema.items);
141
+ const leafSchema = getSchemaAtPath(itemSchema, pathInItem);
142
+ const value = newData === null && !schemaAllowsNull(leafSchema)
143
+ ? undefined
144
+ : newData;
145
+ const oldItem = oldData[index];
146
+ const newItem = setValueAtPath(oldItem, pathInItem, value);
147
+ return resetDependentFields(oldItem, newItem, itemSchema, rootSchema);
148
+ }
101
149
  render() {
102
150
  const arrayProps = Object.assign(Object.assign({}, this.props), { onChange: this.handleChange });
103
151
  return React.createElement('div', { ref: this.setWrapper }, React.createElement(BaseArrayField, arrayProps));
104
152
  }
105
153
  }
154
+ function getSchemaAtPath(schema, pathSegments) {
155
+ let current = schema;
156
+ for (const segment of pathSegments) {
157
+ if (!current) {
158
+ return;
159
+ }
160
+ current = resolveSchemaSegment(current, segment);
161
+ }
162
+ return current;
163
+ }
164
+ function resolveSchemaSegment(schema, segment) {
165
+ var _a;
166
+ if (typeof segment === 'number') {
167
+ const items = schema.items;
168
+ return Array.isArray(items) ? items[segment] : items;
169
+ }
170
+ return (_a = schema.properties) === null || _a === void 0 ? void 0 : _a[segment];
171
+ }
172
+ /**
173
+ * Return a copy of `target` with `value` written at the location described
174
+ * by `pathSegments`, creating intermediate containers as needed. Numeric
175
+ * segments address arrays; string segments address object keys. `target`
176
+ * is not mutated.
177
+ *
178
+ * @param target
179
+ * @param pathSegments
180
+ * @param value
181
+ * @example
182
+ * setValueAtPath({ a: { b: 1 } }, ['a', 'b'], 2); // → { a: { b: 2 } }
183
+ * setValueAtPath([{ x: 1 }], [0, 'x'], 9); // → [{ x: 9 }]
184
+ */
185
+ function setValueAtPath(target, pathSegments, value) {
186
+ if (pathSegments.length === 0) {
187
+ return value;
188
+ }
189
+ const clone = cloneContainer(target, pathSegments[0]);
190
+ set(clone, pathSegments, value);
191
+ return clone;
192
+ }
193
+ function cloneContainer(target, firstSegment) {
194
+ if (Array.isArray(target) || isPlainObject(target)) {
195
+ return cloneDeep(target);
196
+ }
197
+ if (typeof firstSegment === 'number') {
198
+ return [];
199
+ }
200
+ return {};
201
+ }
@@ -69,3 +69,18 @@ export function isCustomObjectSchema(schema) {
69
69
  function isAdditionalProperty(schema) {
70
70
  return schema[ADDITIONAL_PROPERTY_FLAG] === true;
71
71
  }
72
+ /**
73
+ * Check whether a schema permits `null` as a valid value. Used to decide
74
+ * whether a cleared field value should be preserved as `null` or converted
75
+ * to `undefined` so the property can be removed from the form data.
76
+ *
77
+ * @param schema - the schema to check
78
+ * @returns true if the schema's type is `'null'` or includes `'null'`
79
+ */
80
+ export const schemaAllowsNull = (schema) => {
81
+ const type = schema === null || schema === void 0 ? void 0 : schema.type;
82
+ if (!type) {
83
+ return false;
84
+ }
85
+ return Array.isArray(type) ? type.includes('null') : type === 'null';
86
+ };
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import { getDefaultRegistry } from "@rjsf/core";
3
3
  import { isEmpty } from "lodash-es";
4
4
  import { hasValue, isFieldInvalid, isFieldRequired, getErrorText, } from "../validation-display";
5
- import { resetDependentFields } from "./field-helpers";
5
+ import { resetDependentFields, schemaAllowsNull } from "./field-helpers";
6
6
  import { FieldTemplate } from "../templates";
7
7
  import { getHelpComponent } from "../help";
8
8
  import { TimePicker } from "../widgets/time-picker";
@@ -34,7 +34,7 @@ const BaseSchemaField = defaultFields.SchemaField;
34
34
  * @returns whether or not null should be changed to undefined
35
35
  */
36
36
  const shouldChangeToUndefined = (value, schema) => {
37
- return value === null && !schema.type.includes('null');
37
+ return value === null && !schemaAllowsNull(schema);
38
38
  };
39
39
  const hasCustomComponent = (schema) => {
40
40
  var _a, _b;
@@ -121,9 +121,26 @@ export class SchemaField extends React.Component {
121
121
  const { formData, schema } = this.props;
122
122
  const { rootSchema } = this.props.registry;
123
123
  this.setState({ modified: true });
124
+ if (this.isDeepLeafChange(path)) {
125
+ this.props.onChange(data, path);
126
+ return;
127
+ }
124
128
  const newData = resetDependentFields(formData, data, schema, rootSchema);
125
129
  this.props.onChange(newData, path);
126
130
  }
131
+ /**
132
+ * RJSF v6's built-in ArrayField shares one onChange handler across all
133
+ * descendants, so changes to leaves deep inside an array item bubble
134
+ * through this SchemaField with a path that is deeper than its own.
135
+ * The enclosing ArrayField rebuilds the affected item and runs
136
+ * `resetDependentFields` at the item level (see array-field.ts), so we
137
+ * must pass the partial leaf data through untouched here.
138
+ * @param path
139
+ */
140
+ isDeepLeafChange(path) {
141
+ const { fieldPathId } = this.props;
142
+ return !!fieldPathId && path.length > fieldPathId.path.length;
143
+ }
127
144
  buildCustomComponentProps() {
128
145
  const { disabled, readonly, name, registry, schema: rawSchema, errorSchema, fieldPathId, } = this.props;
129
146
  const schema = rawSchema;
@@ -306,3 +306,121 @@ export const nestedArrayObjectSchema = {
306
306
  },
307
307
  },
308
308
  };
309
+ export const topLevelStringCustomComponentSchema = {
310
+ type: 'object',
311
+ properties: {
312
+ icon: {
313
+ type: 'string',
314
+ title: 'Icon',
315
+ lime: { component: { name: 'limel-input-field' } },
316
+ },
317
+ },
318
+ };
319
+ export const arrayItemWithDependenciesSchema = {
320
+ type: 'object',
321
+ properties: {
322
+ entries: {
323
+ type: 'array',
324
+ title: 'Entries',
325
+ items: {
326
+ type: 'object',
327
+ properties: {
328
+ useA: {
329
+ type: 'boolean',
330
+ title: 'Use A',
331
+ },
332
+ },
333
+ required: ['useA'],
334
+ dependencies: {
335
+ useA: {
336
+ oneOf: [
337
+ {
338
+ properties: {
339
+ useA: { const: true },
340
+ valueA: {
341
+ type: 'string',
342
+ title: 'Value A',
343
+ },
344
+ },
345
+ },
346
+ {
347
+ properties: {
348
+ useA: { const: false },
349
+ valueB: {
350
+ type: 'string',
351
+ title: 'Value B',
352
+ },
353
+ },
354
+ },
355
+ ],
356
+ },
357
+ },
358
+ },
359
+ },
360
+ },
361
+ };
362
+ export const arrayItemWithDependenciesAndCustomConfigSchema = {
363
+ type: 'object',
364
+ properties: {
365
+ entries: {
366
+ type: 'array',
367
+ title: 'Entries',
368
+ items: {
369
+ type: 'object',
370
+ properties: {
371
+ useA: {
372
+ type: 'boolean',
373
+ title: 'Use A',
374
+ },
375
+ config: {
376
+ type: 'object',
377
+ title: 'Config',
378
+ additionalProperties: { type: 'string' },
379
+ lime: {
380
+ component: { name: 'limel-input-field' },
381
+ },
382
+ },
383
+ },
384
+ required: ['useA'],
385
+ dependencies: {
386
+ useA: {
387
+ oneOf: [
388
+ {
389
+ properties: {
390
+ useA: { const: true },
391
+ valueA: {
392
+ type: 'string',
393
+ title: 'Value A',
394
+ },
395
+ },
396
+ },
397
+ {
398
+ properties: { useA: { const: false } },
399
+ },
400
+ ],
401
+ },
402
+ },
403
+ additionalProperties: true,
404
+ },
405
+ },
406
+ },
407
+ };
408
+ export const nestedStringCustomComponentSchema = {
409
+ type: 'object',
410
+ properties: {
411
+ views: {
412
+ type: 'array',
413
+ title: 'Views',
414
+ items: {
415
+ type: 'object',
416
+ properties: {
417
+ icon: {
418
+ type: 'string',
419
+ title: 'Icon',
420
+ lime: { component: { name: 'limel-input-field' } },
421
+ },
422
+ },
423
+ },
424
+ },
425
+ },
426
+ };