jsswift 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/dist/jsswift.js CHANGED
@@ -648,7 +648,10 @@
648
648
 
649
649
  // scroll lock + focus
650
650
  if (opts.lockScroll) lockScroll();
651
- if (opts.autoFocus !== false) setTimeout(() => focusFirst(panel), 0);
651
+ if (opts.autoFocus !== false) setTimeout(() => {
652
+ // Respect focus deliberately assigned by an onOpen callback.
653
+ if (panel.isConnected && stack.get(id) === entry && !panel.contains(document.activeElement)) focusFirst(panel);
654
+ }, 0);
652
655
 
653
656
  // positioning (for menus/tooltips)
654
657
  const position = (nextOpts = null) => {
@@ -7510,6 +7513,7 @@ const unitCover = (v, name = 'size') => {
7510
7513
  // props: { model: rod OR [get,set] signal OR plain, placeholder, type }
7511
7514
 
7512
7515
 
7516
+ let uiFieldMessageId = 0;
7513
7517
  UI.FormField = (props = {}) => {
7514
7518
 
7515
7519
  applyCommonProps(props);
@@ -7642,6 +7646,14 @@ const unitCover = (v, name = 'size') => {
7642
7646
  return true;
7643
7647
  };
7644
7648
  let messageEl = null;
7649
+ const messageId = `jsswift-field-message-${++uiFieldMessageId}`;
7650
+ const syncDescription = () => {
7651
+ if (!controlEl?.setAttribute) return;
7652
+ const ids = [uiUnwrap(props.ariaDescribedBy), messageEl?.id].filter(Boolean).join(" ").trim();
7653
+ if (ids) controlEl.setAttribute("aria-describedby", [...new Set(ids.split(/\s+/))].join(" "));
7654
+ else controlEl.removeAttribute("aria-describedby");
7655
+ };
7656
+ wrap._syncDescription = syncDescription;
7645
7657
  const renderMessage = () => {
7646
7658
  const statusOrder = [
7647
7659
  { key: "error", value: resolveValue(props.error), slot: "errorMessage", className: "cms-error" },
@@ -7669,7 +7681,7 @@ const unitCover = (v, name = 'size') => {
7669
7681
  ? renderSlotToArray(slots, active.slot, ctx, active.value)
7670
7682
  : [];
7671
7683
  const nextEl = nodes.length
7672
- ? _.div({ class: active?.className }, ...nodes)
7684
+ ? _.div({ id: messageId, class: active?.className }, ...nodes)
7673
7685
  : null;
7674
7686
 
7675
7687
  const stateKeys = ["error", "warning", "success", "note"];
@@ -7682,6 +7694,7 @@ const unitCover = (v, name = 'size') => {
7682
7694
  wrap.appendChild(nextEl);
7683
7695
  }
7684
7696
  messageEl = nextEl;
7697
+ syncDescription();
7685
7698
  };
7686
7699
 
7687
7700
  // state updater
@@ -7823,6 +7836,30 @@ const unitCover = (v, name = 'size') => {
7823
7836
  return el;
7824
7837
  };
7825
7838
 
7839
+ // Public field attributes belong to the interactive control, not its wrapper.
7840
+ function uiConfigureFieldControl(field, control, props, native = false) {
7841
+ field.focus = (options) => control.focus(options);
7842
+ const set = (name, value) => {
7843
+ if (value == null || value === false) control.removeAttribute(name);
7844
+ else control.setAttribute(name, String(value));
7845
+ };
7846
+ JSswift.reactive.effect(() => {
7847
+ const label = uiUnwrap(props.ariaLabel) ?? (typeof props.label === "string" ? props.label : null);
7848
+ set("aria-label", label);
7849
+ set("aria-labelledby", uiUnwrap(props.ariaLabelledBy));
7850
+ field._syncDescription?.();
7851
+ const invalid = props.ariaInvalid == null ? !!uiUnwrap(props.error) : !!uiUnwrap(props.ariaInvalid);
7852
+ control.setAttribute("aria-invalid", String(invalid));
7853
+ const required = !!uiUnwrap(props.required);
7854
+ control.setAttribute("aria-required", String(required));
7855
+ if (native) {
7856
+ control.required = required;
7857
+ for (const name of ["min", "max", "step", "pattern", "minLength", "maxLength"])
7858
+ set(name.toLowerCase(), uiUnwrap(props[name]));
7859
+ }
7860
+ }, "UI.Field:controlAttributes");
7861
+ }
7862
+
7826
7863
  UI.Input = (props = {}) => {
7827
7864
  const slots = props.slots || {};
7828
7865
  const fieldProps = {
@@ -7984,6 +8021,7 @@ const unitCover = (v, name = 'size') => {
7984
8021
 
7985
8022
  // expose reference
7986
8023
  field._input = input;
8024
+ uiConfigureFieldControl(field, input, props, true);
7987
8025
  uiRegisterShortcode(input, props, {
7988
8026
  isEnabled: () => !input.disabled,
7989
8027
  action: () => uiFocusShortcutTarget(input, { selectText: !!props.selectOnShortcode })
@@ -9381,6 +9419,7 @@ const unitCover = (v, name = 'size') => {
9381
9419
  // refresh has-value when value changes
9382
9420
  JSswift.reactive.effect(() => { field._refresh?.(); }, "UI.Select:fieldRefresh");
9383
9421
 
9422
+ uiConfigureFieldControl(field, root, props);
9384
9423
  field._select = root;
9385
9424
  field._dispose = root._dispose;
9386
9425
  JSswift._registerCleanup?.(field, field._dispose);