@estiva-app/ui 0.49.0 → 0.50.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.
package/README.md CHANGED
@@ -79,6 +79,8 @@ instead. There are seven — six block, one warns:
79
79
  `className` or an inner box's class prop — a colour, a text size, a border, a
80
80
  corner, a shadow — and names the part's look props (`PART_LOOK_PROPS`). Only
81
81
  placement goes through (`PLACEMENT`), and `EmptyState` takes no padding.
82
+ A `Form` takes no layout of its own, and sits in no spaced box beside other
83
+ things: what comes before its fields goes inside it.
82
84
  - `estiva/no-handmade-header` refuses a column's header bar drawn by hand: a
83
85
  row first in a column, with its own side padding and a title, where the column
84
86
  fills its height or the row has a bar's height or hairline. It reads the
@@ -218,6 +218,10 @@ var isFieldsLayout = (token) => {
218
218
  const { variants, utility } = splitClass(token);
219
219
  return !variants.some((variant) => REACHING_VARIANT.test(variant)) && FIELDS_LAYOUT.test(utility);
220
220
  };
221
+ var isSpacing = (token) => {
222
+ const { variants, utility } = splitClass(token);
223
+ return !variants.some((variant) => REACHING_VARIANT.test(variant)) && /^(?:gap|gap-x|gap-y|space-x|space-y)-/.test(utility);
224
+ };
221
225
  var isPadding = (token) => {
222
226
  const { variants, utility } = splitClass(token);
223
227
  return !variants.some((variant) => REACHING_VARIANT.test(variant)) && /^p[xytrblse]?-/.test(utility);
@@ -635,6 +639,7 @@ var noRestyledPart = {
635
639
  restyledStyle: "{{keys}} in the `style` of {{where}} changes how it looks. A part of @estiva-app/ui is placed from outside, never restyled, by class or by style: only space, size, flex and grid, and position pass in.{{use}} A look for what is around it goes on your own element around it.",
636
640
  emptyStatePadding: "{{classes}} pads {{where}}. EmptyState takes no padding (Katerina, 14 September): its room comes from the box its rows live in, so put the padding on that box.",
637
641
  formLayout: '{{classes}} lays out the fields of {{where}}. A Form places its own fields, 24px apart (Katerina, 28 September), so every form reads the same: remove it. A form that is not a list of fields (a field and its button, a composer) says `layout="free"` and lays itself out.',
642
+ formSpacedFromOutside: "{{classes}} on the box around `Form` spaces the form from what is beside it, so the gap above its first field is not the form\u2019s 24px. Put what is beside it inside the `Form`, as one block before the fields, and drop the box: the Form puts 24px between them (Katerina, 1 October; MoveDialog in Peek does this).",
638
643
  ...ESCAPE_MESSAGES
639
644
  }
640
645
  },
@@ -688,6 +693,26 @@ var noRestyledPart = {
688
693
  const where = binding.via && binding.via !== binding.part ? `\`${binding.via}\` (it hands its props to \`${binding.part}\`)` : `\`${binding.part}\``;
689
694
  context.report({ loc: at.loc, messageId: "restyledStyle", data: { keys: code([...new Set(keys)]), where, use } });
690
695
  };
696
+ const checkSpacedFromOutside = (opening) => {
697
+ const form = opening.parent;
698
+ const box = form?.parent;
699
+ if (form?.type !== "JSXElement" || box?.type !== "JSXElement") return;
700
+ const beside = children(box, "children").filter((node) => {
701
+ if (!node || node === form) return false;
702
+ if (node.type === "JSXText") return String(node.value).trim() !== "";
703
+ if (node.type === "JSXExpressionContainer") return child(node, "expression")?.type !== "JSXEmptyExpression";
704
+ return true;
705
+ });
706
+ if (!beside.length) return;
707
+ const boxOpening = child(box, "openingElement");
708
+ const spacing = children(boxOpening, "attributes").flatMap((attribute3) => {
709
+ if (attribute3?.type !== "JSXAttribute") return [];
710
+ const prop = nameOf(child(attribute3, "name"));
711
+ return prop === "className" || prop === "bodyClassName" ? classesOf(child(attribute3, "value"), scopeOf).filter(isSpacing) : [];
712
+ });
713
+ if (!spacing.length || escaped(opening)) return;
714
+ context.report({ loc: opening.loc, messageId: "formSpacedFromOutside", data: { classes: code([...new Set(spacing)]) } });
715
+ };
691
716
  return {
692
717
  Program(program) {
693
718
  locals = moduleParts(file, program, parser, /* @__PURE__ */ new Set([file])).locals;
@@ -696,6 +721,7 @@ var noRestyledPart = {
696
721
  const element = ruleNode;
697
722
  const binding = locals && bindingOfTag(child(element, "name"), locals);
698
723
  if (!binding) return;
724
+ if (binding.part === "Form" && binding.props === "all" && placesItsFields(element)) checkSpacedFromOutside(element);
699
725
  for (const attribute3 of children(element, "attributes")) {
700
726
  if (!attribute3) continue;
701
727
  if (attribute3.type === "JSXAttribute") {