@blukavantui/community 1.0.1 → 1.1.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 (160) hide show
  1. package/README.md +11 -5
  2. package/dist/build-audit.json +181 -53
  3. package/dist/checkbox.css +1 -0
  4. package/dist/checkbox.d.ts +2 -0
  5. package/dist/checkbox.js +1 -0
  6. package/dist/chunk-3PNBQVYS.js +1 -0
  7. package/dist/chunk-5ENZVZGF.js +0 -0
  8. package/dist/chunk-AL2ETLJV.js +1 -0
  9. package/dist/chunk-BN3PDE5G.js +0 -0
  10. package/dist/chunk-EOKNLCNS.js +1 -0
  11. package/dist/chunk-EPZGLHPD.js +0 -0
  12. package/dist/chunk-FNX33PDD.js +0 -0
  13. package/dist/chunk-GXHANADL.js +1 -0
  14. package/dist/chunk-HO6CYWED.js +0 -0
  15. package/dist/chunk-IJ62ILKE.js +0 -0
  16. package/dist/chunk-IO22GWON.js +0 -0
  17. package/dist/chunk-IZJN3EF7.js +1 -0
  18. package/dist/chunk-JVE6CXCJ.js +1 -0
  19. package/dist/chunk-MYVLBALF.js +1 -0
  20. package/dist/chunk-OPMD7QNV.js +1 -0
  21. package/dist/chunk-RWBLICYX.js +0 -0
  22. package/dist/chunk-RYD6RHRX.js +0 -0
  23. package/dist/chunk-UN2SLVFG.js +1 -0
  24. package/dist/chunk-VJVFHL6P.js +0 -0
  25. package/dist/chunk-XWXEH3SW.js +1 -0
  26. package/dist/chunk-YCLR3IE3.js +0 -0
  27. package/dist/columns2.css +1 -0
  28. package/dist/columns2.d.ts +2 -0
  29. package/dist/columns2.js +1 -0
  30. package/dist/columns3.css +1 -0
  31. package/dist/columns3.d.ts +2 -0
  32. package/dist/columns3.js +1 -0
  33. package/dist/columns4.css +1 -0
  34. package/dist/columns4.d.ts +2 -0
  35. package/dist/columns4.js +1 -0
  36. package/dist/content.js +1 -1
  37. package/dist/core.js +1 -1
  38. package/dist/data.css +63 -0
  39. package/dist/data.d.ts +1 -0
  40. package/dist/data.js +1 -1
  41. package/dist/date-picker.css +5 -5
  42. package/dist/date-picker.js +1 -1
  43. package/dist/date-range-picker.css +5 -5
  44. package/dist/date-range-picker.js +1 -1
  45. package/dist/date-time-picker.css +5 -5
  46. package/dist/date-time-picker.js +1 -1
  47. package/dist/draft-components.json +77 -0
  48. package/dist/feedback.js +1 -1
  49. package/dist/form-field.css +1 -0
  50. package/dist/form-field.d.ts +2 -0
  51. package/dist/form-field.js +1 -0
  52. package/dist/form-layout/choice.d.ts +15 -0
  53. package/dist/form-layout/layout.d.ts +12 -0
  54. package/dist/form-layout/select.d.ts +3 -0
  55. package/dist/form-layout/shared.d.ts +21 -0
  56. package/dist/form-layout/table.d.ts +20 -0
  57. package/dist/form-layout/text.d.ts +3 -0
  58. package/dist/form-layout/types.d.ts +151 -0
  59. package/dist/form-layout.css +62 -0
  60. package/dist/form-layout.d.ts +12 -0
  61. package/dist/form-layout.js +1 -0
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.js +1 -1
  64. package/dist/inputs.css +83 -20
  65. package/dist/inputs.d.ts +8 -0
  66. package/dist/inputs.js +1 -1
  67. package/dist/layout.css +63 -0
  68. package/dist/layout.d.ts +3 -0
  69. package/dist/layout.js +1 -1
  70. package/dist/native/date-picker.js +1 -1
  71. package/dist/native/date-range-picker.js +1 -1
  72. package/dist/native/date-time-picker.js +1 -1
  73. package/dist/native/form-layout.js +1 -0
  74. package/dist/native/time-picker.js +1 -1
  75. package/dist/native-components/form-layout.d.ts +1 -0
  76. package/dist/radio-button.css +1 -0
  77. package/dist/radio-button.d.ts +2 -0
  78. package/dist/radio-button.js +1 -0
  79. package/dist/radio-group.css +1 -0
  80. package/dist/radio-group.d.ts +2 -0
  81. package/dist/radio-group.js +1 -0
  82. package/dist/select.css +1 -0
  83. package/dist/select.d.ts +2 -0
  84. package/dist/select.js +1 -0
  85. package/dist/sizes.json +272 -90
  86. package/dist/styles.css +83 -20
  87. package/dist/switch.css +1 -0
  88. package/dist/switch.d.ts +2 -0
  89. package/dist/switch.js +1 -0
  90. package/dist/table.css +1 -0
  91. package/dist/table.d.ts +2 -0
  92. package/dist/table.js +1 -0
  93. package/dist/text-area.css +1 -0
  94. package/dist/text-area.d.ts +2 -0
  95. package/dist/text-area.js +1 -0
  96. package/dist/text-input.css +1 -0
  97. package/dist/text-input.d.ts +2 -0
  98. package/dist/text-input.js +1 -0
  99. package/dist/time-picker.css +5 -5
  100. package/dist/time-picker.js +1 -1
  101. package/docs/FORM-LAYOUT-ESSENTIALS.md +122 -0
  102. package/docs/PUBLISHING.md +129 -25
  103. package/draft-components.json +77 -0
  104. package/edition.json +44 -7
  105. package/npm-shrinkwrap.json +2 -2
  106. package/package.json +76 -10
  107. package/scripts/build.mjs +16 -8
  108. package/src/checkbox.ts +2 -0
  109. package/src/columns2.ts +2 -0
  110. package/src/columns3.ts +2 -0
  111. package/src/columns4.ts +2 -0
  112. package/src/data.ts +1 -0
  113. package/src/form-field.ts +2 -0
  114. package/src/form-layout/choice.ts +66 -0
  115. package/src/form-layout/layout.ts +40 -0
  116. package/src/form-layout/select.ts +97 -0
  117. package/src/form-layout/shared.ts +57 -0
  118. package/src/form-layout/table.ts +86 -0
  119. package/src/form-layout/text.ts +73 -0
  120. package/src/form-layout/types.ts +59 -0
  121. package/src/form-layout.ts +12 -0
  122. package/src/index.ts +2 -0
  123. package/src/inputs.ts +8 -0
  124. package/src/layout.ts +3 -0
  125. package/src/native-components/form-layout.ts +21 -0
  126. package/src/radio-button.ts +2 -0
  127. package/src/radio-group.ts +2 -0
  128. package/src/runtime/date-picker.js +21 -12
  129. package/src/runtime/date-range-picker.js +21 -12
  130. package/src/runtime/date-time-picker.js +21 -12
  131. package/src/runtime/time-picker.js +21 -12
  132. package/src/select.ts +2 -0
  133. package/src/switch.ts +2 -0
  134. package/src/table.ts +2 -0
  135. package/src/text-area.ts +2 -0
  136. package/src/text-input.ts +2 -0
  137. package/src/types.ts +0 -12
  138. package/styles/checkbox.css +1 -0
  139. package/styles/columns2.css +1 -0
  140. package/styles/columns3.css +1 -0
  141. package/styles/columns4.css +1 -0
  142. package/styles/date-picker.css +5 -5
  143. package/styles/date-range-picker.css +5 -5
  144. package/styles/date-time-picker.css +5 -5
  145. package/styles/form-field.css +1 -0
  146. package/styles/form-layout.css +62 -0
  147. package/styles/radio-button.css +1 -0
  148. package/styles/radio-group.css +1 -0
  149. package/styles/select.css +1 -0
  150. package/styles/switch.css +1 -0
  151. package/styles/table.css +1 -0
  152. package/styles/text-area.css +1 -0
  153. package/styles/text-input.css +1 -0
  154. package/styles/time-picker.css +5 -5
  155. package/test/form-layout.test.mjs +28 -0
  156. package/test/isolation.test.mjs +2 -2
  157. package/dist/chunk-2TCNAQC6.js +0 -1
  158. package/dist/chunk-CTLZIWMX.js +0 -1
  159. package/dist/chunk-MHHJZYXW.js +0 -1
  160. package/dist/chunk-XDYBNO4P.js +0 -1
@@ -1,4 +1,4 @@
1
- // ../blu-grid/docs/scripts/advanced/shared.js
1
+ // docs/scripts/advanced/shared.js
2
2
  import { state, register } from "./kernel.js";
3
3
  var shared = state("advanced", () => ({ instances: /* @__PURE__ */ new Map(), serial: 0 }));
4
4
  var uid = () => `blu-advanced-${++shared.serial}`;
@@ -91,7 +91,7 @@ function createAPI(kind, init2, update2) {
91
91
  }, destroy }));
92
92
  }
93
93
 
94
- // ../blu-grid/docs/scripts/advanced/date-utils.js
94
+ // docs/scripts/advanced/date-utils.js
95
95
  var pad = (n) => String(n).padStart(2, "0");
96
96
  function dateValue(d) {
97
97
  return String(d.getFullYear()).padStart(4, "0") + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
@@ -172,7 +172,7 @@ function validateRange(start, end, options = {}) {
172
172
  return "";
173
173
  }
174
174
 
175
- // ../blu-grid/docs/scripts/advanced/pickers.js
175
+ // docs/scripts/advanced/pickers.js
176
176
  var formatOptions = ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY"];
177
177
  var today = () => dateValue(/* @__PURE__ */ new Date());
178
178
  var datePart = (value) => String(value || "").split("T")[0];
@@ -256,14 +256,15 @@ function positionPopup(s) {
256
256
  const below = Math.max(0, top + height - edge - anchor.bottom - gap), above = Math.max(0, anchor.top - gap - top - edge);
257
257
  const natural = panel.scrollHeight + 2, up = natural > below && above > below, room = Math.max(0, height - edge * 2);
258
258
  panel.style.maxHeight = room + "px";
259
- panel.style.left = Math.max(left + edge, Math.min(anchor.left, left + width - edge - panelWidth)) + "px";
259
+ const fields = s.fieldRow.getBoundingClientRect();
260
+ panel.style.left = Math.max(left + edge, Math.min(fields.left + (fields.width - panelWidth) / 2, left + width - edge - panelWidth)) + "px";
260
261
  const preferred = up ? anchor.top - gap - natural : anchor.bottom + gap;
261
262
  panel.style.top = Math.max(top + edge, Math.min(preferred, top + height - edge - Math.min(natural, room))) + "px";
262
263
  panel.dataset.placement = up ? "top" : "bottom";
263
264
  }
264
265
  function focusPopup(s) {
265
266
  const timeField = s.timeOnly || s.dateTime && s.anchor === s.fields[1].input;
266
- (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus();
267
+ (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus({ preventScroll: true });
267
268
  }
268
269
  function commit(s, first, end, time, keepOpen = false) {
269
270
  const issue = validate(s, first, end, time);
@@ -287,7 +288,7 @@ function commit(s, first, end, time, keepOpen = false) {
287
288
  s.inputSignature = JSON.stringify(s.range ? [s.start, s.end] : [s.value]);
288
289
  paintInputs(s);
289
290
  if (!keepOpen) close(s);
290
- if (returnToInput) s.fields[0].input.focus();
291
+ if (returnToInput) s.fields[0].input.focus({ preventScroll: true });
291
292
  s.notice.textContent = s.range ? first ? labelDate(s, first) + " to " + labelDate(s, end) : "Date range cleared" : value ? "Selected " + (s.timeOnly ? formatTime(value, s.cycle) : labelDate(s, first) + (s.hasTime ? " at " + formatTime(time, s.cycle) : "")) : "Selection cleared";
292
293
  if (changed) {
293
294
  if (s.range) emit(s, "Change", first, end);
@@ -391,11 +392,18 @@ function appendOption(select, value, label) {
391
392
  return option;
392
393
  }
393
394
  function selectField(s, label, name) {
394
- const wrap = node("label", "blu-picker__select-field"), caption = node("span", "", label), select = node("select", "blu-picker__input");
395
+ const wrap = node("label", "blu-picker__select-field"), caption = node("span", "blu-picker__select-caption", label), select = node("select", "blu-picker__input");
395
396
  select.id = s.uid + "-" + name;
396
397
  wrap.append(caption, select);
397
398
  return { wrap, select };
398
399
  }
400
+ function focusPopupButton(event) {
401
+ if (event.button !== 0) return;
402
+ const target = event.target.closest?.("button");
403
+ if (!target || target.disabled) return;
404
+ event.preventDefault();
405
+ target.focus({ preventScroll: true });
406
+ }
399
407
  function focusDay(s, value) {
400
408
  const min = datePart(s.options.Min) || "0001-01-01", max = datePart(s.options.Max) || "9999-12-31";
401
409
  value = value < min ? min : value > max ? max : value;
@@ -403,7 +411,7 @@ function focusDay(s, value) {
403
411
  s.focusDate = value;
404
412
  s.month = value.slice(0, 7);
405
413
  paintCalendar(s);
406
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
414
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
407
415
  }
408
416
  function keyDay(s, event, value) {
409
417
  if (event.altKey || event.ctrlKey || event.metaKey) return;
@@ -441,7 +449,7 @@ function chooseDay(s, value) {
441
449
  return;
442
450
  }
443
451
  paintCalendar(s);
444
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
452
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
445
453
  paintDraft(s);
446
454
  }
447
455
  function paintDraft(s) {
@@ -594,6 +602,7 @@ function init(s) {
594
602
  attr(s.dialog, "aria-modal", "false");
595
603
  if (s.dialog.showPopover) attr(s.dialog, "popover", "manual");
596
604
  s.root.append(s.dialog);
605
+ s.dialog.addEventListener("mousedown", focusPopupButton);
597
606
  s.title = node("h2", "blu-advanced-sr");
598
607
  s.title.id = s.uid + "-title";
599
608
  s.dialog.append(s.title);
@@ -606,7 +615,7 @@ function init(s) {
606
615
  const month = selectField(s, "Month", "month");
607
616
  s.monthSelect = month.select;
608
617
  const year = node("label", "blu-picker__select-field");
609
- year.append(node("span", "", "Year"));
618
+ year.append(node("span", "blu-picker__select-caption", "Year"));
610
619
  s.yearInput = node("input", "blu-picker__input");
611
620
  s.yearInput.type = "number";
612
621
  s.yearInput.step = "1";
@@ -804,8 +813,8 @@ function update(s) {
804
813
  else {
805
814
  paintCalendar(s);
806
815
  paintTimes(s);
807
- if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus();
808
- else if (focusId) document.getElementById(focusId)?.focus();
816
+ if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus({ preventScroll: true });
817
+ else if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });
809
818
  positionPopup(s);
810
819
  }
811
820
  }
@@ -1,4 +1,4 @@
1
- // ../blu-grid/docs/scripts/advanced/shared.js
1
+ // docs/scripts/advanced/shared.js
2
2
  import { state, register } from "./kernel.js";
3
3
  var shared = state("advanced", () => ({ instances: /* @__PURE__ */ new Map(), serial: 0 }));
4
4
  var uid = () => `blu-advanced-${++shared.serial}`;
@@ -91,7 +91,7 @@ function createAPI(kind, init2, update2) {
91
91
  }, destroy }));
92
92
  }
93
93
 
94
- // ../blu-grid/docs/scripts/advanced/date-utils.js
94
+ // docs/scripts/advanced/date-utils.js
95
95
  var pad = (n) => String(n).padStart(2, "0");
96
96
  function dateValue(d) {
97
97
  return String(d.getFullYear()).padStart(4, "0") + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
@@ -172,7 +172,7 @@ function validateRange(start, end, options = {}) {
172
172
  return "";
173
173
  }
174
174
 
175
- // ../blu-grid/docs/scripts/advanced/pickers.js
175
+ // docs/scripts/advanced/pickers.js
176
176
  var formatOptions = ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY"];
177
177
  var today = () => dateValue(/* @__PURE__ */ new Date());
178
178
  var datePart = (value) => String(value || "").split("T")[0];
@@ -256,14 +256,15 @@ function positionPopup(s) {
256
256
  const below = Math.max(0, top + height - edge - anchor.bottom - gap), above = Math.max(0, anchor.top - gap - top - edge);
257
257
  const natural = panel.scrollHeight + 2, up = natural > below && above > below, room = Math.max(0, height - edge * 2);
258
258
  panel.style.maxHeight = room + "px";
259
- panel.style.left = Math.max(left + edge, Math.min(anchor.left, left + width - edge - panelWidth)) + "px";
259
+ const fields = s.fieldRow.getBoundingClientRect();
260
+ panel.style.left = Math.max(left + edge, Math.min(fields.left + (fields.width - panelWidth) / 2, left + width - edge - panelWidth)) + "px";
260
261
  const preferred = up ? anchor.top - gap - natural : anchor.bottom + gap;
261
262
  panel.style.top = Math.max(top + edge, Math.min(preferred, top + height - edge - Math.min(natural, room))) + "px";
262
263
  panel.dataset.placement = up ? "top" : "bottom";
263
264
  }
264
265
  function focusPopup(s) {
265
266
  const timeField = s.timeOnly || s.dateTime && s.anchor === s.fields[1].input;
266
- (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus();
267
+ (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus({ preventScroll: true });
267
268
  }
268
269
  function commit(s, first, end, time, keepOpen = false) {
269
270
  const issue = validate(s, first, end, time);
@@ -287,7 +288,7 @@ function commit(s, first, end, time, keepOpen = false) {
287
288
  s.inputSignature = JSON.stringify(s.range ? [s.start, s.end] : [s.value]);
288
289
  paintInputs(s);
289
290
  if (!keepOpen) close(s);
290
- if (returnToInput) s.fields[0].input.focus();
291
+ if (returnToInput) s.fields[0].input.focus({ preventScroll: true });
291
292
  s.notice.textContent = s.range ? first ? labelDate(s, first) + " to " + labelDate(s, end) : "Date range cleared" : value ? "Selected " + (s.timeOnly ? formatTime(value, s.cycle) : labelDate(s, first) + (s.hasTime ? " at " + formatTime(time, s.cycle) : "")) : "Selection cleared";
292
293
  if (changed) {
293
294
  if (s.range) emit(s, "Change", first, end);
@@ -391,11 +392,18 @@ function appendOption(select, value, label) {
391
392
  return option;
392
393
  }
393
394
  function selectField(s, label, name) {
394
- const wrap = node("label", "blu-picker__select-field"), caption = node("span", "", label), select = node("select", "blu-picker__input");
395
+ const wrap = node("label", "blu-picker__select-field"), caption = node("span", "blu-picker__select-caption", label), select = node("select", "blu-picker__input");
395
396
  select.id = s.uid + "-" + name;
396
397
  wrap.append(caption, select);
397
398
  return { wrap, select };
398
399
  }
400
+ function focusPopupButton(event) {
401
+ if (event.button !== 0) return;
402
+ const target = event.target.closest?.("button");
403
+ if (!target || target.disabled) return;
404
+ event.preventDefault();
405
+ target.focus({ preventScroll: true });
406
+ }
399
407
  function focusDay(s, value) {
400
408
  const min = datePart(s.options.Min) || "0001-01-01", max = datePart(s.options.Max) || "9999-12-31";
401
409
  value = value < min ? min : value > max ? max : value;
@@ -403,7 +411,7 @@ function focusDay(s, value) {
403
411
  s.focusDate = value;
404
412
  s.month = value.slice(0, 7);
405
413
  paintCalendar(s);
406
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
414
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
407
415
  }
408
416
  function keyDay(s, event, value) {
409
417
  if (event.altKey || event.ctrlKey || event.metaKey) return;
@@ -441,7 +449,7 @@ function chooseDay(s, value) {
441
449
  return;
442
450
  }
443
451
  paintCalendar(s);
444
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
452
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
445
453
  paintDraft(s);
446
454
  }
447
455
  function paintDraft(s) {
@@ -594,6 +602,7 @@ function init(s) {
594
602
  attr(s.dialog, "aria-modal", "false");
595
603
  if (s.dialog.showPopover) attr(s.dialog, "popover", "manual");
596
604
  s.root.append(s.dialog);
605
+ s.dialog.addEventListener("mousedown", focusPopupButton);
597
606
  s.title = node("h2", "blu-advanced-sr");
598
607
  s.title.id = s.uid + "-title";
599
608
  s.dialog.append(s.title);
@@ -606,7 +615,7 @@ function init(s) {
606
615
  const month = selectField(s, "Month", "month");
607
616
  s.monthSelect = month.select;
608
617
  const year = node("label", "blu-picker__select-field");
609
- year.append(node("span", "", "Year"));
618
+ year.append(node("span", "blu-picker__select-caption", "Year"));
610
619
  s.yearInput = node("input", "blu-picker__input");
611
620
  s.yearInput.type = "number";
612
621
  s.yearInput.step = "1";
@@ -804,8 +813,8 @@ function update(s) {
804
813
  else {
805
814
  paintCalendar(s);
806
815
  paintTimes(s);
807
- if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus();
808
- else if (focusId) document.getElementById(focusId)?.focus();
816
+ if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus({ preventScroll: true });
817
+ else if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });
809
818
  positionPopup(s);
810
819
  }
811
820
  }
package/src/select.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {createSelect} from './form-layout/select.js';
2
+ export type * from './form-layout/types.js';
package/src/switch.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {createSwitch} from './form-layout/choice.js';
2
+ export type * from './form-layout/types.js';
package/src/table.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {createTable} from './form-layout/table.js';
2
+ export type * from './form-layout/types.js';
@@ -0,0 +1,2 @@
1
+ export {createTextArea} from './form-layout/text.js';
2
+ export type * from './form-layout/types.js';
@@ -0,0 +1,2 @@
1
+ export {createTextInput} from './form-layout/text.js';
2
+ export type * from './form-layout/types.js';
package/src/types.ts CHANGED
@@ -1285,15 +1285,3 @@ DurationSeconds: number;
1285
1285
  /** Played ranges since the previous checkpoint. Seeking gaps are excluded. */
1286
1286
  Segments: BluVideoWatchedSegment[];
1287
1287
  }
1288
-
1289
-
1290
-
1291
-
1292
-
1293
-
1294
-
1295
-
1296
-
1297
-
1298
-
1299
-
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -6,7 +6,7 @@
6
6
  .blu-picker [hidden],.blu-picker__dialog [hidden] {display:none!important;}
7
7
  .blu-picker__fields {display:flex;flex-wrap:wrap;gap:12px;}
8
8
  .blu-picker__field {flex:1 1 180px;min-width:0;}
9
- .blu-picker__field label,.blu-picker__select-field>span {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
9
+ .blu-picker__field label,.blu-picker__select-caption {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
10
10
  .blu-picker__input {display:block;width:100%;min-width:0;min-height:42px;padding:9px 11px;border:1px solid var(--bluui-control-border,#69788d);border-radius:9px;background:var(--bluui-surface,#fff);color:var(--bluui-text,#182230);font:inherit;line-height:1.4;}
11
11
  .blu-picker__field-control {position:relative;}
12
12
  .blu-picker__field-control>.blu-picker__input {padding-right:38px;}
@@ -29,7 +29,7 @@
29
29
  .blu-picker__dialog::backdrop {background:transparent;pointer-events:none;}
30
30
  .blu-picker__dialog-head {display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;}
31
31
  .blu-picker__dialog-head h2 {font-size:16px;line-height:1.4;font-family:var(--blu-font-heading-family,inherit);margin:0;overflow-wrap:anywhere;}
32
- .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 72px 32px;align-items:end;gap:6px;margin-top:0;}
32
+ .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 88px 32px;align-items:end;gap:6px;margin-top:0;}
33
33
  .blu-picker__calendar-nav>.blu-picker__button {height:36px;padding:2px;font-size:22px;}
34
34
  .blu-picker__select-field {min-width:0;}
35
35
  .blu-picker__caption {font-size:15px;text-align:center;margin:16px 0 8px;}
@@ -54,8 +54,8 @@
54
54
  /* Compact popup controls; editable text stays at 16px to avoid iOS focus zoom. */
55
55
  .blu-picker__dialog .blu-picker__input {min-height:36px;padding:5px 7px;font-size:16px;line-height:1.2;}
56
56
  .blu-picker__dialog .blu-picker__button {min-height:34px;min-width:32px;padding:5px 9px;font-size:13px;line-height:1.25;}
57
- .blu-picker__dialog .blu-picker__select-field>span {font-size:11px;margin-bottom:2px;}
58
- .blu-picker__calendar-nav .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
57
+ .blu-picker__dialog .blu-picker__select-caption {font-size:11px;margin-bottom:2px;}
58
+ .blu-picker__calendar-nav .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
59
59
  .blu-picker__dialog .blu-picker__error {margin-top:4px;font-size:12px;line-height:1.3;}
60
60
  @media(pointer:coarse){
61
61
  .blu-picker__field-control>.blu-picker__input {font-size:16px;min-height:44px;}
@@ -72,7 +72,7 @@
72
72
  .blu-picker__dialog[data-compact=true] .blu-picker__calendar td {height:24px;}
73
73
  .blu-picker__dialog[data-compact=true] .blu-picker__day {min-height:24px;padding:3px 0;}
74
74
  .blu-picker__dialog[data-compact=true] .blu-picker__time-area {margin:4px 0;gap:6px;}
75
- .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
75
+ .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
76
76
  .blu-picker__dialog[data-compact=true] .blu-picker__selection {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
77
77
  .blu-picker__dialog[data-compact=true] .blu-picker__popup-actions {margin-top:4px;gap:4px;}
78
78
  .blu-picker__dialog[data-compact=true] .blu-picker__presets {margin-bottom:4px;gap:4px;}
@@ -6,7 +6,7 @@
6
6
  .blu-picker [hidden],.blu-picker__dialog [hidden] {display:none!important;}
7
7
  .blu-picker__fields {display:flex;flex-wrap:wrap;gap:12px;}
8
8
  .blu-picker__field {flex:1 1 180px;min-width:0;}
9
- .blu-picker__field label,.blu-picker__select-field>span {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
9
+ .blu-picker__field label,.blu-picker__select-caption {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
10
10
  .blu-picker__input {display:block;width:100%;min-width:0;min-height:42px;padding:9px 11px;border:1px solid var(--bluui-control-border,#69788d);border-radius:9px;background:var(--bluui-surface,#fff);color:var(--bluui-text,#182230);font:inherit;line-height:1.4;}
11
11
  .blu-picker__field-control {position:relative;}
12
12
  .blu-picker__field-control>.blu-picker__input {padding-right:38px;}
@@ -29,7 +29,7 @@
29
29
  .blu-picker__dialog::backdrop {background:transparent;pointer-events:none;}
30
30
  .blu-picker__dialog-head {display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;}
31
31
  .blu-picker__dialog-head h2 {font-size:16px;line-height:1.4;font-family:var(--blu-font-heading-family,inherit);margin:0;overflow-wrap:anywhere;}
32
- .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 72px 32px;align-items:end;gap:6px;margin-top:0;}
32
+ .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 88px 32px;align-items:end;gap:6px;margin-top:0;}
33
33
  .blu-picker__calendar-nav>.blu-picker__button {height:36px;padding:2px;font-size:22px;}
34
34
  .blu-picker__select-field {min-width:0;}
35
35
  .blu-picker__caption {font-size:15px;text-align:center;margin:16px 0 8px;}
@@ -54,8 +54,8 @@
54
54
  /* Compact popup controls; editable text stays at 16px to avoid iOS focus zoom. */
55
55
  .blu-picker__dialog .blu-picker__input {min-height:36px;padding:5px 7px;font-size:16px;line-height:1.2;}
56
56
  .blu-picker__dialog .blu-picker__button {min-height:34px;min-width:32px;padding:5px 9px;font-size:13px;line-height:1.25;}
57
- .blu-picker__dialog .blu-picker__select-field>span {font-size:11px;margin-bottom:2px;}
58
- .blu-picker__calendar-nav .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
57
+ .blu-picker__dialog .blu-picker__select-caption {font-size:11px;margin-bottom:2px;}
58
+ .blu-picker__calendar-nav .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
59
59
  .blu-picker__dialog .blu-picker__error {margin-top:4px;font-size:12px;line-height:1.3;}
60
60
  @media(pointer:coarse){
61
61
  .blu-picker__field-control>.blu-picker__input {font-size:16px;min-height:44px;}
@@ -72,7 +72,7 @@
72
72
  .blu-picker__dialog[data-compact=true] .blu-picker__calendar td {height:24px;}
73
73
  .blu-picker__dialog[data-compact=true] .blu-picker__day {min-height:24px;padding:3px 0;}
74
74
  .blu-picker__dialog[data-compact=true] .blu-picker__time-area {margin:4px 0;gap:6px;}
75
- .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
75
+ .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
76
76
  .blu-picker__dialog[data-compact=true] .blu-picker__selection {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
77
77
  .blu-picker__dialog[data-compact=true] .blu-picker__popup-actions {margin-top:4px;gap:4px;}
78
78
  .blu-picker__dialog[data-compact=true] .blu-picker__presets {margin-bottom:4px;gap:4px;}
@@ -6,7 +6,7 @@
6
6
  .blu-picker [hidden],.blu-picker__dialog [hidden] {display:none!important;}
7
7
  .blu-picker__fields {display:flex;flex-wrap:wrap;gap:12px;}
8
8
  .blu-picker__field {flex:1 1 180px;min-width:0;}
9
- .blu-picker__field label,.blu-picker__select-field>span {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
9
+ .blu-picker__field label,.blu-picker__select-caption {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
10
10
  .blu-picker__input {display:block;width:100%;min-width:0;min-height:42px;padding:9px 11px;border:1px solid var(--bluui-control-border,#69788d);border-radius:9px;background:var(--bluui-surface,#fff);color:var(--bluui-text,#182230);font:inherit;line-height:1.4;}
11
11
  .blu-picker__field-control {position:relative;}
12
12
  .blu-picker__field-control>.blu-picker__input {padding-right:38px;}
@@ -29,7 +29,7 @@
29
29
  .blu-picker__dialog::backdrop {background:transparent;pointer-events:none;}
30
30
  .blu-picker__dialog-head {display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;}
31
31
  .blu-picker__dialog-head h2 {font-size:16px;line-height:1.4;font-family:var(--blu-font-heading-family,inherit);margin:0;overflow-wrap:anywhere;}
32
- .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 72px 32px;align-items:end;gap:6px;margin-top:0;}
32
+ .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 88px 32px;align-items:end;gap:6px;margin-top:0;}
33
33
  .blu-picker__calendar-nav>.blu-picker__button {height:36px;padding:2px;font-size:22px;}
34
34
  .blu-picker__select-field {min-width:0;}
35
35
  .blu-picker__caption {font-size:15px;text-align:center;margin:16px 0 8px;}
@@ -54,8 +54,8 @@
54
54
  /* Compact popup controls; editable text stays at 16px to avoid iOS focus zoom. */
55
55
  .blu-picker__dialog .blu-picker__input {min-height:36px;padding:5px 7px;font-size:16px;line-height:1.2;}
56
56
  .blu-picker__dialog .blu-picker__button {min-height:34px;min-width:32px;padding:5px 9px;font-size:13px;line-height:1.25;}
57
- .blu-picker__dialog .blu-picker__select-field>span {font-size:11px;margin-bottom:2px;}
58
- .blu-picker__calendar-nav .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
57
+ .blu-picker__dialog .blu-picker__select-caption {font-size:11px;margin-bottom:2px;}
58
+ .blu-picker__calendar-nav .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
59
59
  .blu-picker__dialog .blu-picker__error {margin-top:4px;font-size:12px;line-height:1.3;}
60
60
  @media(pointer:coarse){
61
61
  .blu-picker__field-control>.blu-picker__input {font-size:16px;min-height:44px;}
@@ -72,7 +72,7 @@
72
72
  .blu-picker__dialog[data-compact=true] .blu-picker__calendar td {height:24px;}
73
73
  .blu-picker__dialog[data-compact=true] .blu-picker__day {min-height:24px;padding:3px 0;}
74
74
  .blu-picker__dialog[data-compact=true] .blu-picker__time-area {margin:4px 0;gap:6px;}
75
- .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
75
+ .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
76
76
  .blu-picker__dialog[data-compact=true] .blu-picker__selection {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
77
77
  .blu-picker__dialog[data-compact=true] .blu-picker__popup-actions {margin-top:4px;gap:4px;}
78
78
  .blu-picker__dialog[data-compact=true] .blu-picker__presets {margin-bottom:4px;gap:4px;}
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1,62 @@
1
+ /* Community Form & Layout Essentials — local draft; no OutSystems UI selectors. */
2
+ .blu-field,.blu-columns,.blu-table{box-sizing:border-box;min-width:0;color:var(--bluui-text,#182230);font-family:var(--blu-font-family,Inter,system-ui,sans-serif);font-size:14px}
3
+ .blu-field *,.blu-columns *,.blu-table *{box-sizing:border-box}
4
+ .blu-field [hidden],.blu-table [hidden]{display:none!important}
5
+ .blu-field{--blu-field-height:42px;--blu-field-padding:12px;display:flex;flex-direction:column;gap:7px;border:0;padding:0;margin:0;min-inline-size:0}
6
+ .blu-field--small{--blu-field-height:34px;--blu-field-padding:9px;font-size:13px}.blu-field--large{--blu-field-height:50px;--blu-field-padding:15px;font-size:16px}
7
+ .blu-field__label{font-weight:600;line-height:1.5;padding:0;margin:0 0 2px}.blu-field__body{min-width:0}
8
+ .blu-field__help,.blu-choice__description,.blu-field__count{font-size:12px;color:var(--bluui-text-muted,#5c6b80);line-height:1.5}.blu-field__error{color:var(--bluui-color-error-text,#b42318);font-size:12px;line-height:1.5}.blu-field__count{text-align:end;margin-top:5px;font-variant-numeric:tabular-nums}
9
+ .blu-text,.blu-textarea,.blu-select__trigger{border:1px solid var(--bluui-control-border,#7b8799);border-radius:var(--blu-radius-md,8px);background:var(--bluui-surface,#fff);color:inherit;transition:border-color .12s,box-shadow .12s}
10
+ .blu-text{display:flex;align-items:center;gap:8px;min-height:var(--blu-field-height);padding:0 var(--blu-field-padding)}
11
+ .blu-text__input{width:100%;min-width:0;border:0!important;box-shadow:none!important;outline:none!important;background:transparent!important;color:inherit;padding:9px 0!important;font:inherit;line-height:1.5;height:auto}
12
+ .blu-text__input::placeholder,.blu-textarea::placeholder{color:var(--bluui-text-muted,#5c6b80);opacity:1}
13
+ .blu-text:focus-within,.blu-textarea:focus-visible,.blu-select__trigger:focus-visible{border-color:var(--bluui-primary,#4f46e5);outline:2px solid var(--bluui-primary,#4f46e5);outline-offset:2px}
14
+ .blu-text__adornment{display:inline-flex;flex:none;gap:6px;align-items:center;color:var(--bluui-text-muted,#5c6b80);white-space:nowrap}.blu-text__adornment img{width:24px;height:24px;object-fit:contain;border-radius:4px}
15
+ .blu-text__action{display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:4px;background:transparent;color:var(--bluui-primary,#4f46e5);font:inherit;font-size:12px;font-weight:600;min-width:26px;min-height:30px;cursor:pointer;white-space:nowrap;padding:3px}
16
+ .blu-text__action:hover{background:var(--bluui-hover,#f0f2f7)}
17
+ .blu-field button:focus-visible,.blu-table button:focus-visible,.blu-table__scroll:focus-visible{outline:2px solid var(--bluui-primary,#4f46e5);outline-offset:3px}
18
+ .blu-textarea{display:block;width:100%;min-height:var(--blu-field-height);padding:10px var(--blu-field-padding);font:inherit;line-height:1.6}
19
+ .blu-field[data-invalid=true] .blu-text,.blu-field[data-invalid=true] .blu-textarea,.blu-field[data-invalid=true] .blu-select__trigger{border-color:var(--bluui-color-error-text,#b42318)}
20
+ .blu-field[data-disabled=true]{opacity:.6}.blu-field input:disabled,.blu-field textarea:disabled,.blu-field button:disabled{cursor:not-allowed}
21
+ .blu-field input[readonly],.blu-field textarea[readonly]{cursor:default}
22
+ .blu-choice{display:flex;align-items:center;position:relative;gap:10px;min-height:32px;cursor:pointer;line-height:1.5}.blu-choice__input{position:absolute;inline-size:1px;block-size:1px;clip-path:inset(50%);overflow:hidden;white-space:nowrap;opacity:0}
23
+ .blu-choice__mark{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:19px;height:19px;border:1.5px solid var(--bluui-control-border,#7b8799);border-radius:5px;background:var(--bluui-surface,#fff);color:var(--bluui-on-primary,#fff)}
24
+ .blu-choice__input:checked+.blu-choice__mark,.blu-choice__input:indeterminate+.blu-choice__mark{background:var(--bluui-primary,#4f46e5);border-color:var(--bluui-primary,#4f46e5)}
25
+ .blu-choice--checkbox .blu-choice__input:checked+.blu-choice__mark:after{content:'';width:5px;height:9px;border:solid currentColor;border-width:0 2px 2px 0;transform:translateY(-1px) rotate(45deg)}
26
+ .blu-choice__input:indeterminate+.blu-choice__mark:after{content:'';width:9px;height:2px;background:currentColor;transform:none;border:0}
27
+ .blu-choice__input:focus-visible+.blu-choice__mark{outline:2px solid var(--bluui-primary,#4f46e5);outline-offset:3px}
28
+ .blu-choice:has(input:disabled){opacity:.6;cursor:not-allowed}.blu-field[data-disabled=true] .blu-choice:has(input:disabled){opacity:1}
29
+ .blu-choice--radio .blu-choice__mark{border-radius:50%}.blu-choice--radio .blu-choice__input:checked+.blu-choice__mark:after{content:'';width:7px;height:7px;border-radius:50%;background:currentColor}
30
+ .blu-choice--switch .blu-choice__mark{width:38px;height:22px;border-radius:20px;background:var(--bluui-control-border,#7b8799);padding:2px;justify-content:flex-start}
31
+ .blu-choice--switch .blu-choice__mark:after{content:'';height:16px;width:16px;background:#fff;border-radius:50%;transition:transform .15s}
32
+ .blu-choice--switch .blu-choice__input:checked+.blu-choice__mark:after{transform:translateX(14px)}.blu-choice--switch .blu-choice__input:checked+.blu-choice__mark{background:var(--bluui-primary,#4f46e5)}
33
+ .blu-choice__state{margin-inline-start:auto;font-size:12px;color:var(--bluui-text-muted,#5c6b80)}.blu-choice__description{display:block}
34
+ .blu-radio-group{display:flex;flex-direction:column;gap:10px}.blu-radio-group--horizontal{flex-direction:row;flex-wrap:wrap;gap:12px 24px}
35
+ .blu-select{position:relative;display:flex;gap:5px}.blu-select__native{position:absolute;width:1px;height:1px;opacity:0;clip-path:inset(50%);pointer-events:none}
36
+ .blu-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0;min-height:var(--blu-field-height);width:100%;padding:9px var(--blu-field-padding);font:inherit;text-align:start;cursor:pointer}
37
+ .blu-select__trigger:after{content:'';width:7px;height:7px;border:solid currentColor;border-width:0 1.5px 1.5px 0;transform:translateY(-2px) rotate(45deg);flex-shrink:0}.blu-select__trigger[data-placeholder=true]{color:var(--bluui-text-muted,#5c6b80)}
38
+ .blu-select__popup{box-sizing:border-box;position:absolute;inset:calc(100% + 6px) 0 auto;z-index:1000;max-height:320px;overflow:auto;margin:0;padding:6px;border:1px solid var(--bluui-border-strong,#b8c4d4);border-radius:10px;background:var(--bluui-surface,#fff);box-shadow:var(--bluui-menu-shadow,0 10px 30px #17203326);color:var(--bluui-text,#182230);font:inherit}
39
+ .blu-select__search{width:100%;min-height:38px;padding:8px 10px;border:1px solid var(--bluui-control-border,#7b8799);border-radius:6px;background:var(--bluui-surface,#fff);color:inherit;font:inherit;margin-bottom:5px;outline-offset:2px}
40
+ .blu-select__option{padding:9px 10px;border-radius:5px;cursor:pointer;line-height:1.5}.blu-select__option[aria-selected=true]{font-weight:600;color:var(--bluui-primary,#4f46e5)}.blu-select__option[data-active=true]{background:var(--bluui-selected,#e9e6ff)}.blu-select__option[aria-disabled=true]{opacity:.5;cursor:not-allowed}
41
+ .blu-columns{display:grid;grid-template-columns:var(--blu-columns-tracks,1fr 1fr);gap:var(--blu-columns-gap,24px)}.blu-columns__column{min-width:0;display:flex;flex-direction:column;gap:20px}
42
+ @media(max-width:479px){.blu-columns--stack-small{grid-template-columns:minmax(0,1fr)}}
43
+ @media(max-width:767px){.blu-columns--stack-medium{grid-template-columns:minmax(0,1fr)}}
44
+ @media(max-width:1023px){.blu-columns--stack-large{grid-template-columns:minmax(0,1fr)}}
45
+ .blu-table{border:1px solid var(--bluui-border,#e2e8f0);border-radius:12px;background:var(--bluui-surface,#fff);overflow:hidden}
46
+ .blu-table__scroll{overflow:auto}.blu-table table{width:100%;border-collapse:collapse;font:inherit;text-align:start}.blu-table caption{text-align:start;padding:18px;font-weight:600;font-size:15px}
47
+ .blu-table th{font-size:12px;color:var(--bluui-text-muted,#5c6b80);background:var(--bluui-surface-muted,#f7f8fb);font-weight:600;white-space:nowrap}
48
+ .blu-table th,.blu-table td{padding:14px 18px;border-top:1px solid var(--bluui-border,#e2e8f0);line-height:1.5}
49
+ .blu-table[data-density=Compact] th,.blu-table[data-density=Compact] td{padding:8px 12px}
50
+ .blu-table[data-striped=true] tbody tr:nth-child(even){background:var(--bluui-surface-muted,#f7f8fb)}
51
+ .blu-table__sort{font:inherit;font-weight:600;color:inherit;background:transparent;border:0;padding:0;cursor:pointer;display:inline-flex;align-items:center;gap:12px}
52
+ .blu-table__empty{text-align:center!important;height:140px;color:var(--bluui-text-muted,#5c6b80)}
53
+ .blu-table__footer{display:flex;align-items:center;gap:8px;padding:12px 18px;border-top:1px solid var(--bluui-border,#e2e8f0);font-size:12px;flex-wrap:wrap}.blu-table__footer>span{margin-inline-end:auto;color:var(--bluui-text-muted,#5c6b80)}
54
+ .blu-table-button{border:1px solid var(--bluui-border-strong,#b8c4d4);border-radius:6px;padding:6px 10px;background:var(--bluui-surface,#fff);font:inherit;font-size:12px;color:var(--bluui-text,#182230);cursor:pointer;min-height:30px;white-space:nowrap}.blu-table-button:hover{background:var(--bluui-hover,#f0f2f7)}.blu-table-button:disabled{opacity:.5;cursor:not-allowed}.blu-table__actions{display:flex;gap:6px}
55
+ @media(prefers-reduced-motion:reduce){.blu-field *{transition:none!important}}
56
+ @media(forced-colors:active){.blu-choice__mark{forced-color-adjust:none;border-color:ButtonText;background:Canvas;color:HighlightText}.blu-choice__input:checked+.blu-choice__mark,.blu-choice__input:indeterminate+.blu-choice__mark{background:Highlight;border-color:Highlight}.blu-select__option[data-active=true]{outline:2px solid Highlight}.blu-choice--switch .blu-choice__mark:after{background:ButtonText}.blu-choice--switch .blu-choice__input:checked+.blu-choice__mark:after{background:HighlightText}}
57
+
58
+ /* Equal height is row-scoped; stacked columns retain their content height. */
59
+ .blu-columns.blu-columns--equal-height{align-items:stretch}
60
+ .blu-columns--equal-height > .blu-columns__column > :only-child{flex:1 1 auto}
61
+ .blu-columns--equal-height > .blu-columns__column > [data-placeholder]{display:flex;flex-direction:column}
62
+ .blu-columns--equal-height > .blu-columns__column > [data-placeholder] > :only-child{flex:1 1 auto}
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -0,0 +1 @@
1
+ @import './form-layout.css';
@@ -6,7 +6,7 @@
6
6
  .blu-picker [hidden],.blu-picker__dialog [hidden] {display:none!important;}
7
7
  .blu-picker__fields {display:flex;flex-wrap:wrap;gap:12px;}
8
8
  .blu-picker__field {flex:1 1 180px;min-width:0;}
9
- .blu-picker__field label,.blu-picker__select-field>span {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
9
+ .blu-picker__field label,.blu-picker__select-caption {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
10
10
  .blu-picker__input {display:block;width:100%;min-width:0;min-height:42px;padding:9px 11px;border:1px solid var(--bluui-control-border,#69788d);border-radius:9px;background:var(--bluui-surface,#fff);color:var(--bluui-text,#182230);font:inherit;line-height:1.4;}
11
11
  .blu-picker__field-control {position:relative;}
12
12
  .blu-picker__field-control>.blu-picker__input {padding-right:38px;}
@@ -29,7 +29,7 @@
29
29
  .blu-picker__dialog::backdrop {background:transparent;pointer-events:none;}
30
30
  .blu-picker__dialog-head {display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;}
31
31
  .blu-picker__dialog-head h2 {font-size:16px;line-height:1.4;font-family:var(--blu-font-heading-family,inherit);margin:0;overflow-wrap:anywhere;}
32
- .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 72px 32px;align-items:end;gap:6px;margin-top:0;}
32
+ .blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 88px 32px;align-items:end;gap:6px;margin-top:0;}
33
33
  .blu-picker__calendar-nav>.blu-picker__button {height:36px;padding:2px;font-size:22px;}
34
34
  .blu-picker__select-field {min-width:0;}
35
35
  .blu-picker__caption {font-size:15px;text-align:center;margin:16px 0 8px;}
@@ -54,8 +54,8 @@
54
54
  /* Compact popup controls; editable text stays at 16px to avoid iOS focus zoom. */
55
55
  .blu-picker__dialog .blu-picker__input {min-height:36px;padding:5px 7px;font-size:16px;line-height:1.2;}
56
56
  .blu-picker__dialog .blu-picker__button {min-height:34px;min-width:32px;padding:5px 9px;font-size:13px;line-height:1.25;}
57
- .blu-picker__dialog .blu-picker__select-field>span {font-size:11px;margin-bottom:2px;}
58
- .blu-picker__calendar-nav .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
57
+ .blu-picker__dialog .blu-picker__select-caption {font-size:11px;margin-bottom:2px;}
58
+ .blu-picker__calendar-nav .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
59
59
  .blu-picker__dialog .blu-picker__error {margin-top:4px;font-size:12px;line-height:1.3;}
60
60
  @media(pointer:coarse){
61
61
  .blu-picker__field-control>.blu-picker__input {font-size:16px;min-height:44px;}
@@ -72,7 +72,7 @@
72
72
  .blu-picker__dialog[data-compact=true] .blu-picker__calendar td {height:24px;}
73
73
  .blu-picker__dialog[data-compact=true] .blu-picker__day {min-height:24px;padding:3px 0;}
74
74
  .blu-picker__dialog[data-compact=true] .blu-picker__time-area {margin:4px 0;gap:6px;}
75
- .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-field>span {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
75
+ .blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
76
76
  .blu-picker__dialog[data-compact=true] .blu-picker__selection {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
77
77
  .blu-picker__dialog[data-compact=true] .blu-picker__popup-actions {margin-top:4px;gap:4px;}
78
78
  .blu-picker__dialog[data-compact=true] .blu-picker__presets {margin-bottom:4px;gap:4px;}
@@ -0,0 +1,28 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {build} from 'esbuild';
4
+ import {readFile} from 'node:fs/promises';
5
+ import vm from 'node:vm';
6
+ const built=await build({entryPoints:['src/form-layout/table.ts'],bundle:true,format:'esm',platform:'node',write:false});
7
+ const {tableRows}=await import('data:text/javascript;base64,'+Buffer.from(built.outputFiles[0].text).toString('base64'));
8
+ const options={Caption:'Records',Columns:[{Key:'value',Header:'Value',Sortable:true}],Rows:[{value:10},{value:null},{value:2},{value:2}],SortKey:'value'};
9
+ test('Table sorts numerically without mutating records, keeps equal rows stable, and keeps nulls last',()=>{
10
+ const sorted=tableRows(options).rows;assert.deepEqual(sorted.map(r=>r.value),[2,2,10,null]);assert.equal(sorted[0],options.Rows[2]);assert.equal(sorted[1],options.Rows[3]);assert.deepEqual(options.Rows.map(r=>r.value),[10,null,2,2]);assert.deepEqual(tableRows({...options,SortDirection:'Descending'}).rows.map(r=>r.value),[10,2,2,null]);
11
+ });
12
+ test('Table ignores non-sortable keys and safely clamps paging after data shrinks',()=>{
13
+ assert.deepEqual(tableRows({...options,Columns:[{Key:'value',Header:'Value'}]}).rows,options.Rows);
14
+ assert.deepEqual(tableRows({...options,PageSize:2,Page:100}),{rows:[options.Rows[0],options.Rows[1]],page:2,pages:2,total:4,start:3,end:4});
15
+ assert.equal(tableRows({...options,Rows:[],Page:20,PageSize:5}).page,1);assert.equal(tableRows({...options,Page:NaN,PageSize:Infinity}).page,1);
16
+ });
17
+ test('every draft component has a selective entry and the Table does not load AG Grid',async()=>{
18
+ const draft=JSON.parse(await readFile('draft-components.json','utf8')),pkg=JSON.parse(await readFile('package.json','utf8'));
19
+ const sizes=JSON.parse(await readFile('dist/sizes.json','utf8'));
20
+ for(const c of draft.components){assert.ok(pkg.exports['./'+c.slug]);await readFile('dist/'+c.slug+'.d.ts');}
21
+ const code=(await Promise.all(sizes.typescript['table'].files.map(f=>readFile('dist/'+f,'utf8')))).join('\n');
22
+ assert.ok(!/ClientSideRowModel|BluGrid|ag-grid/.test(code));assert.ok(sizes.typescript['table'].bytes<20000);
23
+ });
24
+ test('draft native runtime registers independently without changing commercial globals',async()=>{
25
+ const commercial={untouched:true},window={BluUIRuntime:commercial,BluUITheme:{version:'1.0.0',setMode(){},getMode(){return 'Light';},mountSelector(){}}};const context=vm.createContext({window,Map,Set,WeakMap,WeakSet,Promise,console});
26
+ vm.runInContext(await readFile('dist/native/form-layout.js','utf8'),context);
27
+ assert.equal(window.BluUIRuntime,commercial);assert.equal(typeof window.BluUICommunity.sync,'function');assert.equal(typeof window.BluUICommunity.destroy,'function');
28
+ });
@@ -4,8 +4,8 @@ import {readFile,readdir} from 'node:fs/promises';
4
4
  import vm from 'node:vm';
5
5
  const edition=JSON.parse(await readFile('edition.json','utf8'));
6
6
  const sizes=JSON.parse(await readFile('dist/sizes.json','utf8'));
7
- test('all seven groups expose exactly the approved 55 Community components',()=>{
8
- const names=Object.values(edition.nativeModules).flat();assert.equal(names.length,55);assert.equal(new Set(names).size,55);assert.deepEqual([...names].sort(),[...edition.components].sort());assert.equal(Object.keys(edition.nativeModules).length,7);
7
+ test('all seven groups expose the declared release component set without duplicates',()=>{
8
+ const names=Object.values(edition.nativeModules).flat();const expected=edition.version==='1.1.0'?67:55;assert.equal(names.length,expected);assert.equal(new Set(names).size,expected);assert.deepEqual([...names].sort(),[...edition.components].sort());assert.equal(Object.keys(edition.nativeModules).length,7);
9
9
  });
10
10
  test('non-grid entrypoints do not pull the AG Grid engine or unrelated component factories',async()=>{
11
11
  for(const name of ['chat','chat-widget','float-button','date-picker','time-picker','date-time-picker','date-range-picker','font-provider','shape-mask','tree-select','transfer-list','core','layout','inputs','feedback','data']){