@jsswift/ui 1.0.48 → 1.0.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsswift/ui",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "JSswift UI layer built on top of @jsswift/core",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,6 +38,6 @@
38
38
  "src"
39
39
  ],
40
40
  "peerDependencies": {
41
- "@jsswift/core": "^1.0.48"
41
+ "@jsswift/core": "^1.0.50"
42
42
  }
43
43
  }
@@ -1092,6 +1092,7 @@
1092
1092
  // props: { model: rod OR [get,set] signal OR plain, placeholder, type }
1093
1093
 
1094
1094
 
1095
+ let uiFieldMessageId = 0;
1095
1096
  UI.FormField = (props = {}) => {
1096
1097
 
1097
1098
  applyCommonProps(props);
@@ -1224,6 +1225,14 @@
1224
1225
  return true;
1225
1226
  };
1226
1227
  let messageEl = null;
1228
+ const messageId = `jsswift-field-message-${++uiFieldMessageId}`;
1229
+ const syncDescription = () => {
1230
+ if (!controlEl?.setAttribute) return;
1231
+ const ids = [uiUnwrap(props.ariaDescribedBy), messageEl?.id].filter(Boolean).join(" ").trim();
1232
+ if (ids) controlEl.setAttribute("aria-describedby", [...new Set(ids.split(/\s+/))].join(" "));
1233
+ else controlEl.removeAttribute("aria-describedby");
1234
+ };
1235
+ wrap._syncDescription = syncDescription;
1227
1236
  const renderMessage = () => {
1228
1237
  const statusOrder = [
1229
1238
  { key: "error", value: resolveValue(props.error), slot: "errorMessage", className: "cms-error" },
@@ -1251,7 +1260,7 @@
1251
1260
  ? renderSlotToArray(slots, active.slot, ctx, active.value)
1252
1261
  : [];
1253
1262
  const nextEl = nodes.length
1254
- ? _.div({ class: active?.className }, ...nodes)
1263
+ ? _.div({ id: messageId, class: active?.className }, ...nodes)
1255
1264
  : null;
1256
1265
 
1257
1266
  const stateKeys = ["error", "warning", "success", "note"];
@@ -1264,6 +1273,7 @@
1264
1273
  wrap.appendChild(nextEl);
1265
1274
  }
1266
1275
  messageEl = nextEl;
1276
+ syncDescription();
1267
1277
  };
1268
1278
 
1269
1279
  // state updater
@@ -1405,6 +1415,30 @@
1405
1415
  return el;
1406
1416
  };
1407
1417
 
1418
+ // Public field attributes belong to the interactive control, not its wrapper.
1419
+ function uiConfigureFieldControl(field, control, props, native = false) {
1420
+ field.focus = (options) => control.focus(options);
1421
+ const set = (name, value) => {
1422
+ if (value == null || value === false) control.removeAttribute(name);
1423
+ else control.setAttribute(name, String(value));
1424
+ };
1425
+ JSswift.reactive.effect(() => {
1426
+ const label = uiUnwrap(props.ariaLabel) ?? (typeof props.label === "string" ? props.label : null);
1427
+ set("aria-label", label);
1428
+ set("aria-labelledby", uiUnwrap(props.ariaLabelledBy));
1429
+ field._syncDescription?.();
1430
+ const invalid = props.ariaInvalid == null ? !!uiUnwrap(props.error) : !!uiUnwrap(props.ariaInvalid);
1431
+ control.setAttribute("aria-invalid", String(invalid));
1432
+ const required = !!uiUnwrap(props.required);
1433
+ control.setAttribute("aria-required", String(required));
1434
+ if (native) {
1435
+ control.required = required;
1436
+ for (const name of ["min", "max", "step", "pattern", "minLength", "maxLength"])
1437
+ set(name.toLowerCase(), uiUnwrap(props[name]));
1438
+ }
1439
+ }, "UI.Field:controlAttributes");
1440
+ }
1441
+
1408
1442
  UI.Input = (props = {}) => {
1409
1443
  const slots = props.slots || {};
1410
1444
  const fieldProps = {
@@ -1566,6 +1600,7 @@
1566
1600
 
1567
1601
  // expose reference
1568
1602
  field._input = input;
1603
+ uiConfigureFieldControl(field, input, props, true);
1569
1604
  uiRegisterShortcode(input, props, {
1570
1605
  isEnabled: () => !input.disabled,
1571
1606
  action: () => uiFocusShortcutTarget(input, { selectText: !!props.selectOnShortcode })
@@ -2963,6 +2998,7 @@
2963
2998
  // refresh has-value when value changes
2964
2999
  JSswift.reactive.effect(() => { field._refresh?.(); }, "UI.Select:fieldRefresh");
2965
3000
 
3001
+ uiConfigureFieldControl(field, root, props);
2966
3002
  field._select = root;
2967
3003
  field._dispose = root._dispose;
2968
3004
  JSswift._registerCleanup?.(field, field._dispose);