@almadar/ui 4.28.0 → 4.29.1

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.
@@ -27962,6 +27962,8 @@ var init_Form = __esm({
27962
27962
  const [collapsedSections, setCollapsedSections] = React109__namespace.default.useState(
27963
27963
  /* @__PURE__ */ new Set()
27964
27964
  );
27965
+ const [submitError, setSubmitError] = React109__namespace.default.useState(null);
27966
+ const formRef = React109__namespace.default.useRef(null);
27965
27967
  const formMode = props.mode;
27966
27968
  const mountedRef = React109__namespace.default.useRef(false);
27967
27969
  if (!mountedRef.current) {
@@ -28087,6 +28089,7 @@ var init_Form = __esm({
28087
28089
  };
28088
28090
  const handleSubmit = (e) => {
28089
28091
  e.preventDefault();
28092
+ setSubmitError(null);
28090
28093
  debug("forms", "submit-enter", {
28091
28094
  mode: formMode,
28092
28095
  submitEvent,
@@ -28104,6 +28107,37 @@ var init_Form = __esm({
28104
28107
  eventBus.emit(`UI:${onSubmit}`, payload);
28105
28108
  }
28106
28109
  };
28110
+ const handleInvalid = (e) => {
28111
+ const target = e.target;
28112
+ const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
28113
+ const fieldMessage = target.validationMessage || "Invalid value";
28114
+ debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
28115
+ queueMicrotask(() => {
28116
+ const form = formRef.current;
28117
+ if (!form) return;
28118
+ const invalidEls = Array.from(
28119
+ form.querySelectorAll(
28120
+ ":invalid"
28121
+ )
28122
+ );
28123
+ if (invalidEls.length === 0) return;
28124
+ const missing = invalidEls.map(
28125
+ (el) => el.getAttribute("data-field-name") ?? el.name ?? ""
28126
+ );
28127
+ const messages = invalidEls.map((el) => ({
28128
+ field: el.getAttribute("data-field-name") ?? el.name ?? "",
28129
+ message: el.validationMessage
28130
+ }));
28131
+ const summary = missing.length === 1 ? `${missing[0]}: ${messages[0]?.message}` : `Please fix ${missing.length} fields: ${missing.join(", ")}`;
28132
+ setSubmitError(summary);
28133
+ eventBus.emit("UI:VALIDATION_FAILED", {
28134
+ submitEvent,
28135
+ missing,
28136
+ messages,
28137
+ summary
28138
+ });
28139
+ });
28140
+ };
28107
28141
  const handleCancel = () => {
28108
28142
  eventBus.emit(`UI:${cancelEvent}`);
28109
28143
  eventBus.emit("UI:CLOSE");
@@ -28207,7 +28241,8 @@ var init_Form = __esm({
28207
28241
  "data-field-name": fieldName,
28208
28242
  required: field.required,
28209
28243
  disabled: isLoading,
28210
- placeholder: field.placeholder
28244
+ placeholder: field.placeholder,
28245
+ pattern: field.pattern
28211
28246
  };
28212
28247
  switch (inputType) {
28213
28248
  case "checkbox":
@@ -28303,7 +28338,9 @@ var init_Form = __esm({
28303
28338
  ...commonProps,
28304
28339
  type: "email",
28305
28340
  value: String(currentValue),
28306
- onChange: (e) => handleChange(fieldName, e.target.value)
28341
+ onChange: (e) => handleChange(fieldName, e.target.value),
28342
+ minLength: field.min,
28343
+ maxLength: field.max
28307
28344
  }
28308
28345
  );
28309
28346
  case "url":
@@ -28313,7 +28350,9 @@ var init_Form = __esm({
28313
28350
  ...commonProps,
28314
28351
  type: "url",
28315
28352
  value: String(currentValue),
28316
- onChange: (e) => handleChange(fieldName, e.target.value)
28353
+ onChange: (e) => handleChange(fieldName, e.target.value),
28354
+ minLength: field.min,
28355
+ maxLength: field.max
28317
28356
  }
28318
28357
  );
28319
28358
  case "password":
@@ -28323,7 +28362,9 @@ var init_Form = __esm({
28323
28362
  ...commonProps,
28324
28363
  type: "password",
28325
28364
  value: String(currentValue),
28326
- onChange: (e) => handleChange(fieldName, e.target.value)
28365
+ onChange: (e) => handleChange(fieldName, e.target.value),
28366
+ minLength: field.min,
28367
+ maxLength: field.max
28327
28368
  }
28328
28369
  );
28329
28370
  case "text":
@@ -28336,8 +28377,7 @@ var init_Form = __esm({
28336
28377
  value: String(currentValue),
28337
28378
  onChange: (e) => handleChange(fieldName, e.target.value),
28338
28379
  minLength: field.min,
28339
- maxLength: field.max,
28340
- pattern: field.pattern
28380
+ maxLength: field.max
28341
28381
  }
28342
28382
  );
28343
28383
  }
@@ -28345,12 +28385,14 @@ var init_Form = __esm({
28345
28385
  return /* @__PURE__ */ jsxRuntime.jsxs(
28346
28386
  "form",
28347
28387
  {
28348
- noValidate: true,
28388
+ ref: formRef,
28349
28389
  "data-pattern": "form-section",
28350
28390
  className: cn(layoutStyles[layout], gapStyles8[gap], className),
28351
28391
  onSubmit: handleSubmit,
28392
+ onInvalid: handleInvalid,
28352
28393
  ...props,
28353
28394
  children: [
28395
+ submitError && /* @__PURE__ */ jsxRuntime.jsx(exports.Alert, { variant: "error", className: "mb-4", children: submitError }),
28354
28396
  error && /* @__PURE__ */ jsxRuntime.jsx(exports.Alert, { variant: "error", className: "mb-4", children: error.message || t("error.occurred") }),
28355
28397
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
28356
28398
  schemaFields,
@@ -27917,6 +27917,8 @@ var init_Form = __esm({
27917
27917
  const [collapsedSections, setCollapsedSections] = React109__default.useState(
27918
27918
  /* @__PURE__ */ new Set()
27919
27919
  );
27920
+ const [submitError, setSubmitError] = React109__default.useState(null);
27921
+ const formRef = React109__default.useRef(null);
27920
27922
  const formMode = props.mode;
27921
27923
  const mountedRef = React109__default.useRef(false);
27922
27924
  if (!mountedRef.current) {
@@ -28042,6 +28044,7 @@ var init_Form = __esm({
28042
28044
  };
28043
28045
  const handleSubmit = (e) => {
28044
28046
  e.preventDefault();
28047
+ setSubmitError(null);
28045
28048
  debug("forms", "submit-enter", {
28046
28049
  mode: formMode,
28047
28050
  submitEvent,
@@ -28059,6 +28062,37 @@ var init_Form = __esm({
28059
28062
  eventBus.emit(`UI:${onSubmit}`, payload);
28060
28063
  }
28061
28064
  };
28065
+ const handleInvalid = (e) => {
28066
+ const target = e.target;
28067
+ const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
28068
+ const fieldMessage = target.validationMessage || "Invalid value";
28069
+ debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
28070
+ queueMicrotask(() => {
28071
+ const form = formRef.current;
28072
+ if (!form) return;
28073
+ const invalidEls = Array.from(
28074
+ form.querySelectorAll(
28075
+ ":invalid"
28076
+ )
28077
+ );
28078
+ if (invalidEls.length === 0) return;
28079
+ const missing = invalidEls.map(
28080
+ (el) => el.getAttribute("data-field-name") ?? el.name ?? ""
28081
+ );
28082
+ const messages = invalidEls.map((el) => ({
28083
+ field: el.getAttribute("data-field-name") ?? el.name ?? "",
28084
+ message: el.validationMessage
28085
+ }));
28086
+ const summary = missing.length === 1 ? `${missing[0]}: ${messages[0]?.message}` : `Please fix ${missing.length} fields: ${missing.join(", ")}`;
28087
+ setSubmitError(summary);
28088
+ eventBus.emit("UI:VALIDATION_FAILED", {
28089
+ submitEvent,
28090
+ missing,
28091
+ messages,
28092
+ summary
28093
+ });
28094
+ });
28095
+ };
28062
28096
  const handleCancel = () => {
28063
28097
  eventBus.emit(`UI:${cancelEvent}`);
28064
28098
  eventBus.emit("UI:CLOSE");
@@ -28162,7 +28196,8 @@ var init_Form = __esm({
28162
28196
  "data-field-name": fieldName,
28163
28197
  required: field.required,
28164
28198
  disabled: isLoading,
28165
- placeholder: field.placeholder
28199
+ placeholder: field.placeholder,
28200
+ pattern: field.pattern
28166
28201
  };
28167
28202
  switch (inputType) {
28168
28203
  case "checkbox":
@@ -28258,7 +28293,9 @@ var init_Form = __esm({
28258
28293
  ...commonProps,
28259
28294
  type: "email",
28260
28295
  value: String(currentValue),
28261
- onChange: (e) => handleChange(fieldName, e.target.value)
28296
+ onChange: (e) => handleChange(fieldName, e.target.value),
28297
+ minLength: field.min,
28298
+ maxLength: field.max
28262
28299
  }
28263
28300
  );
28264
28301
  case "url":
@@ -28268,7 +28305,9 @@ var init_Form = __esm({
28268
28305
  ...commonProps,
28269
28306
  type: "url",
28270
28307
  value: String(currentValue),
28271
- onChange: (e) => handleChange(fieldName, e.target.value)
28308
+ onChange: (e) => handleChange(fieldName, e.target.value),
28309
+ minLength: field.min,
28310
+ maxLength: field.max
28272
28311
  }
28273
28312
  );
28274
28313
  case "password":
@@ -28278,7 +28317,9 @@ var init_Form = __esm({
28278
28317
  ...commonProps,
28279
28318
  type: "password",
28280
28319
  value: String(currentValue),
28281
- onChange: (e) => handleChange(fieldName, e.target.value)
28320
+ onChange: (e) => handleChange(fieldName, e.target.value),
28321
+ minLength: field.min,
28322
+ maxLength: field.max
28282
28323
  }
28283
28324
  );
28284
28325
  case "text":
@@ -28291,8 +28332,7 @@ var init_Form = __esm({
28291
28332
  value: String(currentValue),
28292
28333
  onChange: (e) => handleChange(fieldName, e.target.value),
28293
28334
  minLength: field.min,
28294
- maxLength: field.max,
28295
- pattern: field.pattern
28335
+ maxLength: field.max
28296
28336
  }
28297
28337
  );
28298
28338
  }
@@ -28300,12 +28340,14 @@ var init_Form = __esm({
28300
28340
  return /* @__PURE__ */ jsxs(
28301
28341
  "form",
28302
28342
  {
28303
- noValidate: true,
28343
+ ref: formRef,
28304
28344
  "data-pattern": "form-section",
28305
28345
  className: cn(layoutStyles[layout], gapStyles8[gap], className),
28306
28346
  onSubmit: handleSubmit,
28347
+ onInvalid: handleInvalid,
28307
28348
  ...props,
28308
28349
  children: [
28350
+ submitError && /* @__PURE__ */ jsx(Alert, { variant: "error", className: "mb-4", children: submitError }),
28309
28351
  error && /* @__PURE__ */ jsx(Alert, { variant: "error", className: "mb-4", children: error.message || t("error.occurred") }),
28310
28352
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
28311
28353
  schemaFields,
@@ -26091,6 +26091,8 @@ var init_Form = __esm({
26091
26091
  const [collapsedSections, setCollapsedSections] = React105__namespace.default.useState(
26092
26092
  /* @__PURE__ */ new Set()
26093
26093
  );
26094
+ const [submitError, setSubmitError] = React105__namespace.default.useState(null);
26095
+ const formRef = React105__namespace.default.useRef(null);
26094
26096
  const formMode = props.mode;
26095
26097
  const mountedRef = React105__namespace.default.useRef(false);
26096
26098
  if (!mountedRef.current) {
@@ -26216,6 +26218,7 @@ var init_Form = __esm({
26216
26218
  };
26217
26219
  const handleSubmit = (e) => {
26218
26220
  e.preventDefault();
26221
+ setSubmitError(null);
26219
26222
  debug("forms", "submit-enter", {
26220
26223
  mode: formMode,
26221
26224
  submitEvent,
@@ -26233,6 +26236,37 @@ var init_Form = __esm({
26233
26236
  eventBus.emit(`UI:${onSubmit}`, payload);
26234
26237
  }
26235
26238
  };
26239
+ const handleInvalid = (e) => {
26240
+ const target = e.target;
26241
+ const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
26242
+ const fieldMessage = target.validationMessage || "Invalid value";
26243
+ debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
26244
+ queueMicrotask(() => {
26245
+ const form = formRef.current;
26246
+ if (!form) return;
26247
+ const invalidEls = Array.from(
26248
+ form.querySelectorAll(
26249
+ ":invalid"
26250
+ )
26251
+ );
26252
+ if (invalidEls.length === 0) return;
26253
+ const missing = invalidEls.map(
26254
+ (el) => el.getAttribute("data-field-name") ?? el.name ?? ""
26255
+ );
26256
+ const messages = invalidEls.map((el) => ({
26257
+ field: el.getAttribute("data-field-name") ?? el.name ?? "",
26258
+ message: el.validationMessage
26259
+ }));
26260
+ const summary = missing.length === 1 ? `${missing[0]}: ${messages[0]?.message}` : `Please fix ${missing.length} fields: ${missing.join(", ")}`;
26261
+ setSubmitError(summary);
26262
+ eventBus.emit("UI:VALIDATION_FAILED", {
26263
+ submitEvent,
26264
+ missing,
26265
+ messages,
26266
+ summary
26267
+ });
26268
+ });
26269
+ };
26236
26270
  const handleCancel = () => {
26237
26271
  eventBus.emit(`UI:${cancelEvent}`);
26238
26272
  eventBus.emit("UI:CLOSE");
@@ -26336,7 +26370,8 @@ var init_Form = __esm({
26336
26370
  "data-field-name": fieldName,
26337
26371
  required: field.required,
26338
26372
  disabled: isLoading,
26339
- placeholder: field.placeholder
26373
+ placeholder: field.placeholder,
26374
+ pattern: field.pattern
26340
26375
  };
26341
26376
  switch (inputType) {
26342
26377
  case "checkbox":
@@ -26432,7 +26467,9 @@ var init_Form = __esm({
26432
26467
  ...commonProps,
26433
26468
  type: "email",
26434
26469
  value: String(currentValue),
26435
- onChange: (e) => handleChange(fieldName, e.target.value)
26470
+ onChange: (e) => handleChange(fieldName, e.target.value),
26471
+ minLength: field.min,
26472
+ maxLength: field.max
26436
26473
  }
26437
26474
  );
26438
26475
  case "url":
@@ -26442,7 +26479,9 @@ var init_Form = __esm({
26442
26479
  ...commonProps,
26443
26480
  type: "url",
26444
26481
  value: String(currentValue),
26445
- onChange: (e) => handleChange(fieldName, e.target.value)
26482
+ onChange: (e) => handleChange(fieldName, e.target.value),
26483
+ minLength: field.min,
26484
+ maxLength: field.max
26446
26485
  }
26447
26486
  );
26448
26487
  case "password":
@@ -26452,7 +26491,9 @@ var init_Form = __esm({
26452
26491
  ...commonProps,
26453
26492
  type: "password",
26454
26493
  value: String(currentValue),
26455
- onChange: (e) => handleChange(fieldName, e.target.value)
26494
+ onChange: (e) => handleChange(fieldName, e.target.value),
26495
+ minLength: field.min,
26496
+ maxLength: field.max
26456
26497
  }
26457
26498
  );
26458
26499
  case "text":
@@ -26465,8 +26506,7 @@ var init_Form = __esm({
26465
26506
  value: String(currentValue),
26466
26507
  onChange: (e) => handleChange(fieldName, e.target.value),
26467
26508
  minLength: field.min,
26468
- maxLength: field.max,
26469
- pattern: field.pattern
26509
+ maxLength: field.max
26470
26510
  }
26471
26511
  );
26472
26512
  }
@@ -26474,12 +26514,14 @@ var init_Form = __esm({
26474
26514
  return /* @__PURE__ */ jsxRuntime.jsxs(
26475
26515
  "form",
26476
26516
  {
26477
- noValidate: true,
26517
+ ref: formRef,
26478
26518
  "data-pattern": "form-section",
26479
26519
  className: cn(layoutStyles[layout], gapStyles8[gap], className),
26480
26520
  onSubmit: handleSubmit,
26521
+ onInvalid: handleInvalid,
26481
26522
  ...props,
26482
26523
  children: [
26524
+ submitError && /* @__PURE__ */ jsxRuntime.jsx(Alert, { variant: "error", className: "mb-4", children: submitError }),
26483
26525
  error && /* @__PURE__ */ jsxRuntime.jsx(Alert, { variant: "error", className: "mb-4", children: error.message || t("error.occurred") }),
26484
26526
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
26485
26527
  schemaFields,
@@ -26046,6 +26046,8 @@ var init_Form = __esm({
26046
26046
  const [collapsedSections, setCollapsedSections] = React105__default.useState(
26047
26047
  /* @__PURE__ */ new Set()
26048
26048
  );
26049
+ const [submitError, setSubmitError] = React105__default.useState(null);
26050
+ const formRef = React105__default.useRef(null);
26049
26051
  const formMode = props.mode;
26050
26052
  const mountedRef = React105__default.useRef(false);
26051
26053
  if (!mountedRef.current) {
@@ -26171,6 +26173,7 @@ var init_Form = __esm({
26171
26173
  };
26172
26174
  const handleSubmit = (e) => {
26173
26175
  e.preventDefault();
26176
+ setSubmitError(null);
26174
26177
  debug("forms", "submit-enter", {
26175
26178
  mode: formMode,
26176
26179
  submitEvent,
@@ -26188,6 +26191,37 @@ var init_Form = __esm({
26188
26191
  eventBus.emit(`UI:${onSubmit}`, payload);
26189
26192
  }
26190
26193
  };
26194
+ const handleInvalid = (e) => {
26195
+ const target = e.target;
26196
+ const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
26197
+ const fieldMessage = target.validationMessage || "Invalid value";
26198
+ debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
26199
+ queueMicrotask(() => {
26200
+ const form = formRef.current;
26201
+ if (!form) return;
26202
+ const invalidEls = Array.from(
26203
+ form.querySelectorAll(
26204
+ ":invalid"
26205
+ )
26206
+ );
26207
+ if (invalidEls.length === 0) return;
26208
+ const missing = invalidEls.map(
26209
+ (el) => el.getAttribute("data-field-name") ?? el.name ?? ""
26210
+ );
26211
+ const messages = invalidEls.map((el) => ({
26212
+ field: el.getAttribute("data-field-name") ?? el.name ?? "",
26213
+ message: el.validationMessage
26214
+ }));
26215
+ const summary = missing.length === 1 ? `${missing[0]}: ${messages[0]?.message}` : `Please fix ${missing.length} fields: ${missing.join(", ")}`;
26216
+ setSubmitError(summary);
26217
+ eventBus.emit("UI:VALIDATION_FAILED", {
26218
+ submitEvent,
26219
+ missing,
26220
+ messages,
26221
+ summary
26222
+ });
26223
+ });
26224
+ };
26191
26225
  const handleCancel = () => {
26192
26226
  eventBus.emit(`UI:${cancelEvent}`);
26193
26227
  eventBus.emit("UI:CLOSE");
@@ -26291,7 +26325,8 @@ var init_Form = __esm({
26291
26325
  "data-field-name": fieldName,
26292
26326
  required: field.required,
26293
26327
  disabled: isLoading,
26294
- placeholder: field.placeholder
26328
+ placeholder: field.placeholder,
26329
+ pattern: field.pattern
26295
26330
  };
26296
26331
  switch (inputType) {
26297
26332
  case "checkbox":
@@ -26387,7 +26422,9 @@ var init_Form = __esm({
26387
26422
  ...commonProps,
26388
26423
  type: "email",
26389
26424
  value: String(currentValue),
26390
- onChange: (e) => handleChange(fieldName, e.target.value)
26425
+ onChange: (e) => handleChange(fieldName, e.target.value),
26426
+ minLength: field.min,
26427
+ maxLength: field.max
26391
26428
  }
26392
26429
  );
26393
26430
  case "url":
@@ -26397,7 +26434,9 @@ var init_Form = __esm({
26397
26434
  ...commonProps,
26398
26435
  type: "url",
26399
26436
  value: String(currentValue),
26400
- onChange: (e) => handleChange(fieldName, e.target.value)
26437
+ onChange: (e) => handleChange(fieldName, e.target.value),
26438
+ minLength: field.min,
26439
+ maxLength: field.max
26401
26440
  }
26402
26441
  );
26403
26442
  case "password":
@@ -26407,7 +26446,9 @@ var init_Form = __esm({
26407
26446
  ...commonProps,
26408
26447
  type: "password",
26409
26448
  value: String(currentValue),
26410
- onChange: (e) => handleChange(fieldName, e.target.value)
26449
+ onChange: (e) => handleChange(fieldName, e.target.value),
26450
+ minLength: field.min,
26451
+ maxLength: field.max
26411
26452
  }
26412
26453
  );
26413
26454
  case "text":
@@ -26420,8 +26461,7 @@ var init_Form = __esm({
26420
26461
  value: String(currentValue),
26421
26462
  onChange: (e) => handleChange(fieldName, e.target.value),
26422
26463
  minLength: field.min,
26423
- maxLength: field.max,
26424
- pattern: field.pattern
26464
+ maxLength: field.max
26425
26465
  }
26426
26466
  );
26427
26467
  }
@@ -26429,12 +26469,14 @@ var init_Form = __esm({
26429
26469
  return /* @__PURE__ */ jsxs(
26430
26470
  "form",
26431
26471
  {
26432
- noValidate: true,
26472
+ ref: formRef,
26433
26473
  "data-pattern": "form-section",
26434
26474
  className: cn(layoutStyles[layout], gapStyles8[gap], className),
26435
26475
  onSubmit: handleSubmit,
26476
+ onInvalid: handleInvalid,
26436
26477
  ...props,
26437
26478
  children: [
26479
+ submitError && /* @__PURE__ */ jsx(Alert, { variant: "error", className: "mb-4", children: submitError }),
26438
26480
  error && /* @__PURE__ */ jsx(Alert, { variant: "error", className: "mb-4", children: error.message || t("error.occurred") }),
26439
26481
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
26440
26482
  schemaFields,
@@ -25658,6 +25658,8 @@ var init_Form = __esm({
25658
25658
  const [collapsedSections, setCollapsedSections] = React104__namespace.default.useState(
25659
25659
  /* @__PURE__ */ new Set()
25660
25660
  );
25661
+ const [submitError, setSubmitError] = React104__namespace.default.useState(null);
25662
+ const formRef = React104__namespace.default.useRef(null);
25661
25663
  const formMode = props.mode;
25662
25664
  const mountedRef = React104__namespace.default.useRef(false);
25663
25665
  if (!mountedRef.current) {
@@ -25783,6 +25785,7 @@ var init_Form = __esm({
25783
25785
  };
25784
25786
  const handleSubmit = (e) => {
25785
25787
  e.preventDefault();
25788
+ setSubmitError(null);
25786
25789
  debug("forms", "submit-enter", {
25787
25790
  mode: formMode,
25788
25791
  submitEvent,
@@ -25800,6 +25803,37 @@ var init_Form = __esm({
25800
25803
  eventBus.emit(`UI:${onSubmit}`, payload);
25801
25804
  }
25802
25805
  };
25806
+ const handleInvalid = (e) => {
25807
+ const target = e.target;
25808
+ const fieldName = target.getAttribute("data-field-name") ?? target.name ?? "";
25809
+ const fieldMessage = target.validationMessage || "Invalid value";
25810
+ debug("forms", "invalid", { mode: formMode, fieldName, fieldMessage });
25811
+ queueMicrotask(() => {
25812
+ const form = formRef.current;
25813
+ if (!form) return;
25814
+ const invalidEls = Array.from(
25815
+ form.querySelectorAll(
25816
+ ":invalid"
25817
+ )
25818
+ );
25819
+ if (invalidEls.length === 0) return;
25820
+ const missing = invalidEls.map(
25821
+ (el) => el.getAttribute("data-field-name") ?? el.name ?? ""
25822
+ );
25823
+ const messages = invalidEls.map((el) => ({
25824
+ field: el.getAttribute("data-field-name") ?? el.name ?? "",
25825
+ message: el.validationMessage
25826
+ }));
25827
+ const summary = missing.length === 1 ? `${missing[0]}: ${messages[0]?.message}` : `Please fix ${missing.length} fields: ${missing.join(", ")}`;
25828
+ setSubmitError(summary);
25829
+ eventBus.emit("UI:VALIDATION_FAILED", {
25830
+ submitEvent,
25831
+ missing,
25832
+ messages,
25833
+ summary
25834
+ });
25835
+ });
25836
+ };
25803
25837
  const handleCancel = () => {
25804
25838
  eventBus.emit(`UI:${cancelEvent}`);
25805
25839
  eventBus.emit("UI:CLOSE");
@@ -25903,7 +25937,8 @@ var init_Form = __esm({
25903
25937
  "data-field-name": fieldName,
25904
25938
  required: field.required,
25905
25939
  disabled: isLoading,
25906
- placeholder: field.placeholder
25940
+ placeholder: field.placeholder,
25941
+ pattern: field.pattern
25907
25942
  };
25908
25943
  switch (inputType) {
25909
25944
  case "checkbox":
@@ -25999,7 +26034,9 @@ var init_Form = __esm({
25999
26034
  ...commonProps,
26000
26035
  type: "email",
26001
26036
  value: String(currentValue),
26002
- onChange: (e) => handleChange(fieldName, e.target.value)
26037
+ onChange: (e) => handleChange(fieldName, e.target.value),
26038
+ minLength: field.min,
26039
+ maxLength: field.max
26003
26040
  }
26004
26041
  );
26005
26042
  case "url":
@@ -26009,7 +26046,9 @@ var init_Form = __esm({
26009
26046
  ...commonProps,
26010
26047
  type: "url",
26011
26048
  value: String(currentValue),
26012
- onChange: (e) => handleChange(fieldName, e.target.value)
26049
+ onChange: (e) => handleChange(fieldName, e.target.value),
26050
+ minLength: field.min,
26051
+ maxLength: field.max
26013
26052
  }
26014
26053
  );
26015
26054
  case "password":
@@ -26019,7 +26058,9 @@ var init_Form = __esm({
26019
26058
  ...commonProps,
26020
26059
  type: "password",
26021
26060
  value: String(currentValue),
26022
- onChange: (e) => handleChange(fieldName, e.target.value)
26061
+ onChange: (e) => handleChange(fieldName, e.target.value),
26062
+ minLength: field.min,
26063
+ maxLength: field.max
26023
26064
  }
26024
26065
  );
26025
26066
  case "text":
@@ -26032,8 +26073,7 @@ var init_Form = __esm({
26032
26073
  value: String(currentValue),
26033
26074
  onChange: (e) => handleChange(fieldName, e.target.value),
26034
26075
  minLength: field.min,
26035
- maxLength: field.max,
26036
- pattern: field.pattern
26076
+ maxLength: field.max
26037
26077
  }
26038
26078
  );
26039
26079
  }
@@ -26041,12 +26081,14 @@ var init_Form = __esm({
26041
26081
  return /* @__PURE__ */ jsxRuntime.jsxs(
26042
26082
  "form",
26043
26083
  {
26044
- noValidate: true,
26084
+ ref: formRef,
26045
26085
  "data-pattern": "form-section",
26046
26086
  className: cn(layoutStyles[layout], gapStyles8[gap], className),
26047
26087
  onSubmit: handleSubmit,
26088
+ onInvalid: handleInvalid,
26048
26089
  ...props,
26049
26090
  children: [
26091
+ submitError && /* @__PURE__ */ jsxRuntime.jsx(Alert, { variant: "error", className: "mb-4", children: submitError }),
26050
26092
  error && /* @__PURE__ */ jsxRuntime.jsx(Alert, { variant: "error", className: "mb-4", children: error.message || t("error.occurred") }),
26051
26093
  sectionElements && sectionElements.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: gap === "sm" ? "sm" : gap === "lg" ? "lg" : "md", children: sectionElements }),
26052
26094
  schemaFields,
@@ -36106,6 +36148,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
36106
36148
  }
36107
36149
  console.log("[TraitStateMachine] Subscribing to events:", Array.from(allEvents));
36108
36150
  const unsubscribes = [];
36151
+ const subscribedBusKeys = /* @__PURE__ */ new Set();
36109
36152
  for (const binding of traitBindings) {
36110
36153
  const traitName = binding.trait.name;
36111
36154
  const orbitalName = orbitalsByTrait?.[traitName];
@@ -36116,6 +36159,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
36116
36159
  continue;
36117
36160
  }
36118
36161
  const selfBusKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
36162
+ if (subscribedBusKeys.has(selfBusKey)) continue;
36163
+ subscribedBusKeys.add(selfBusKey);
36119
36164
  crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
36120
36165
  const unsub = eventBus.on(selfBusKey, (event) => {
36121
36166
  if (event.source && event.source.dispatched) {