@gjermundgaraba-designs/pixel-paper 0.11.0 → 0.12.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 (58) hide show
  1. package/DESIGN.md +11 -11
  2. package/dist/src/emptyState/emptyState.d.ts +4 -0
  3. package/dist/src/emptyState/emptyState.d.ts.map +1 -1
  4. package/dist/src/emptyState/emptyState.js +4 -2
  5. package/dist/src/field/field.d.ts +14 -6
  6. package/dist/src/field/field.d.ts.map +1 -1
  7. package/dist/src/field/field.js +17 -5
  8. package/dist/src/fieldset/fieldset.d.ts +7 -2
  9. package/dist/src/fieldset/fieldset.d.ts.map +1 -1
  10. package/dist/src/fieldset/fieldset.js +10 -4
  11. package/dist/src/lineDiff/diff.d.ts +26 -31
  12. package/dist/src/lineDiff/diff.d.ts.map +1 -1
  13. package/dist/src/lineDiff/diff.js +62 -41
  14. package/dist/src/lineDiff/index.d.ts +2 -2
  15. package/dist/src/lineDiff/index.d.ts.map +1 -1
  16. package/dist/src/lineDiff/index.js +1 -1
  17. package/dist/src/lineDiff/lineDiff.d.ts +14 -0
  18. package/dist/src/lineDiff/lineDiff.d.ts.map +1 -1
  19. package/dist/src/lineDiff/lineDiff.js +43 -21
  20. package/dist/src/passwordField/passwordField.d.ts +11 -6
  21. package/dist/src/passwordField/passwordField.d.ts.map +1 -1
  22. package/dist/src/passwordField/passwordField.js +10 -7
  23. package/dist/src/radioChoices/radioChoices.d.ts +4 -4
  24. package/dist/src/radioChoices/radioChoices.d.ts.map +1 -1
  25. package/dist/src/rail/rail.d.ts +2 -2
  26. package/dist/src/rail/rail.d.ts.map +1 -1
  27. package/dist/src/rail/rail.js +5 -3
  28. package/dist/src/rows/rows.d.ts +7 -2
  29. package/dist/src/rows/rows.d.ts.map +1 -1
  30. package/dist/src/rows/rows.js +3 -3
  31. package/dist/src/selectField/selectField.d.ts +7 -5
  32. package/dist/src/selectField/selectField.d.ts.map +1 -1
  33. package/dist/src/selectField/selectField.js +12 -6
  34. package/dist/src/sheet/sheet.d.ts +3 -2
  35. package/dist/src/sheet/sheet.d.ts.map +1 -1
  36. package/dist/src/sheet/sheet.js +3 -2
  37. package/dist/src/slot/slot.d.ts +2 -1
  38. package/dist/src/slot/slot.d.ts.map +1 -1
  39. package/dist/src/slot/slot.js +2 -2
  40. package/dist/src/textareaField/textareaField.d.ts +15 -5
  41. package/dist/src/textareaField/textareaField.d.ts.map +1 -1
  42. package/dist/src/textareaField/textareaField.js +15 -6
  43. package/package.json +1 -1
  44. package/src/emptyState/emptyState.ts +8 -2
  45. package/src/field/field.ts +37 -8
  46. package/src/fieldset/fieldset.ts +16 -4
  47. package/src/lineDiff/diff.ts +104 -48
  48. package/src/lineDiff/index.ts +2 -2
  49. package/src/lineDiff/lineDiff.ts +76 -31
  50. package/src/passwordField/passwordField.ts +18 -9
  51. package/src/pixel-paper.css +26 -5
  52. package/src/radioChoices/radioChoices.ts +2 -2
  53. package/src/rail/rail.ts +5 -3
  54. package/src/rows/rows.ts +9 -4
  55. package/src/selectField/selectField.ts +17 -17
  56. package/src/sheet/sheet.ts +3 -2
  57. package/src/slot/slot.ts +4 -1
  58. package/src/textareaField/textareaField.ts +39 -9
@@ -1,7 +1,7 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
- import { Array, Option } from "effect";
2
+ import { Array, Option, String } from "effect";
3
3
  import { LOCALE } from "../locale/locale.js";
4
- import { DiffRow, diffLinesOf, foldUnchanged, hasChanges, } from "./diff.js";
4
+ import { DiffRow, diffRowsOf, diffRowsOfPatch, foldUnchanged, hasChanges, } from "./diff.js";
5
5
  // VIEW
6
6
  const DEFAULT_CONTEXT = 2;
7
7
  const COLUMN_COUNT = 4;
@@ -10,34 +10,56 @@ const KIND = {
10
10
  Added: { className: "add", sign: "+", said: "added" },
11
11
  Removed: { className: "del", sign: "−", said: "removed" },
12
12
  };
13
- const numberText = (maybeNumber) => __foldkitBrandViewResult((Option.match(maybeNumber, { onNone: () => __foldkitBrandViewResult((""), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#onNone"), onSome: (lineNumber) => __foldkitBrandViewResult((`${lineNumber}`), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#onSome") })), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#numberText");
14
- const lineKey = (line) => __foldkitBrandViewResult((`${numberText(line.maybeOldNumber)}:${numberText(line.maybeNewNumber)}`), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#lineKey");
15
- const lineView = (line, h) => {
16
- const { className, sign, said } = KIND[line.kind];
17
- return __foldkitBrandViewResult((h.keyed("tr")(lineKey(line), [h.Class(className)], [
18
- h.td([h.Class("ln")], [numberText(line.maybeOldNumber)]),
19
- h.td([h.Class("ln")], [numberText(line.maybeNewNumber)]),
13
+ const lineView = (kind, numbers, text, h) => {
14
+ const { className, sign, said } = KIND[kind];
15
+ return __foldkitBrandViewResult((h.keyed("tr")(`${numbers.old}:${numbers.new}`, [h.Class(className)], [
16
+ h.td([h.Class("ln")], [numbers.old]),
17
+ h.td([h.Class("ln")], [numbers.new]),
20
18
  h.td([h.Class("sign"), h.AriaLabel(said)], [sign]),
21
- h.td([h.Class("line")], [line.text === "" ? " " : line.text]),
19
+ h.td([h.Class("line")], [text === "" ? " " : text]),
22
20
  ])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#lineView");
23
21
  };
24
- const foldView = (count, firstLine, h) => __foldkitBrandViewResult((h.keyed("tr")(`fold-${lineKey(firstLine)}`, [h.Class("fold")], [h.td([h.Colspan(COLUMN_COUNT)], [`${count.toLocaleString(LOCALE)} unchanged lines`])])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#foldView");
22
+ const foldView = (count, firstOldNumber, h) => __foldkitBrandViewResult((h.keyed("tr")(`fold-${firstOldNumber}`, [h.Class("fold")], [
23
+ h.td([h.Colspan(COLUMN_COUNT)], [`${count.toLocaleString(LOCALE)} unchanged ${count === 1 ? "line" : "lines"}`]),
24
+ ])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#foldView");
25
25
  const rowView = (row, h) => __foldkitBrandViewResult((DiffRow.match(row, {
26
- Line: ({ line }) => __foldkitBrandViewResult((lineView(line, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Line"),
27
- Fold: ({ count, firstLine }) => __foldkitBrandViewResult((foldView(count, firstLine, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Fold"),
26
+ Same: ({ text, oldNumber, newNumber }) => __foldkitBrandViewResult((lineView("Same", { old: `${oldNumber}`, new: `${newNumber}` }, text, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Same"),
27
+ Added: ({ text, newNumber }) => __foldkitBrandViewResult((lineView("Added", { old: "", new: `${newNumber}` }, text, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Added"),
28
+ Removed: ({ text, oldNumber }) => __foldkitBrandViewResult((lineView("Removed", { old: `${oldNumber}`, new: "" }, text, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Removed"),
29
+ Fold: ({ count, firstOldNumber }) => __foldkitBrandViewResult((foldView(count, firstOldNumber, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#Fold"),
28
30
  })), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#rowView");
31
+ const noteView = (note, h) => __foldkitBrandViewResult((h.p([h.Class("note-line")], [note])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#noteView");
32
+ const SAME_NOTE = "No difference: both sides hold the same text.";
33
+ const tableView = (rows, label, h) => __foldkitBrandViewResult((h.div([h.Class("diff-wrap")], [
34
+ h.table([h.AriaLabel(label), h.Class("diff")], [
35
+ h.tbody([], Array.map(rows, (row) => __foldkitBrandViewResult((rowView(row, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#anonymous"))),
36
+ ]),
37
+ ])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#tableView");
29
38
  /** A line diff as a ruled table: the old and new numbers, a sign, the line. Added lines sit on
30
39
  * the head tint behind an ink +; removed lines go muted behind a red −, and each sign is
31
40
  * named for screen readers. Two texts that are the same say so in a sentence. */
32
41
  export const view = (config, h) => {
33
42
  const { before, after, label, context = DEFAULT_CONTEXT } = config;
34
- const lines = diffLinesOf(before, after);
35
- if (!hasChanges(lines)) {
36
- return __foldkitBrandViewResult((h.p([h.Class("note-line")], ["No difference: both sides hold the same text."])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#view");
43
+ const rows = diffRowsOf(before, after);
44
+ return __foldkitBrandViewResult((hasChanges(rows)
45
+ ? tableView(foldUnchanged(rows, context), label, h)
46
+ : noteView(SAME_NOTE, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#view");
47
+ };
48
+ /** A unified diff the caller already has (from a server, or `git diff -- <path>`) as `view`'s
49
+ * table: its hunks' lines, numbered from their headers, and the unchanged lines between hunks
50
+ * folded into a counted row. Lines past the last hunk are not in the patch, so nothing counts
51
+ * them. An empty patch says there is no difference, as `view` does; anything but one file
52
+ * with a hunk (text that is not a diff, a binary change, several files) says it cannot be
53
+ * shown as lines. */
54
+ export const patchView = (config, h) => {
55
+ const { patch, label } = config;
56
+ if (String.isEmpty(String.trim(patch))) {
57
+ return __foldkitBrandViewResult((noteView(SAME_NOTE, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#patchView");
58
+ }
59
+ else {
60
+ return __foldkitBrandViewResult((Option.match(diffRowsOfPatch(patch), {
61
+ onNone: () => __foldkitBrandViewResult((noteView("This diff cannot be shown as lines.", h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#onNone"),
62
+ onSome: (rows) => __foldkitBrandViewResult((tableView(rows, label, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#onSome"),
63
+ })), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#patchView");
37
64
  }
38
- return __foldkitBrandViewResult((h.div([h.Class("diff-wrap")], [
39
- h.table([h.AriaLabel(label), h.Class("diff")], [
40
- h.tbody([], Array.map(foldUnchanged(lines, context), (row) => __foldkitBrandViewResult((rowView(row, h)), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#anonymous"))),
41
- ]),
42
- ])), "@gjermundgaraba-designs/pixel-paper/src/lineDiff/lineDiff.js#view");
43
65
  };
@@ -17,8 +17,10 @@ export type ViewConfig<Message> = Readonly<{
17
17
  onToggleShown: Message;
18
18
  /** A sans sentence under the box. */
19
19
  help?: string;
20
- /** Red on the box and on its label; say why in an ErrorSummary above the form. */
21
- isInvalid?: boolean;
20
+ /** Why the value was refused: a red sentence under the control, which the control is
21
+ * described by, with red on its box and its label (`aria-invalid`). Say it again in an
22
+ * ErrorSummary above the form. */
23
+ error?: string | undefined;
22
24
  /** The form is not sent without it: screen readers hear "required" (`aria-required`). */
23
25
  isRequired?: boolean;
24
26
  /** For a new password: four short cells and a word under the box (an empty box is "too
@@ -26,11 +28,14 @@ export type ViewConfig<Message> = Readonly<{
26
28
  strength?: Strength;
27
29
  /** `current-password` to sign in, `new-password` to set one. */
28
30
  autocomplete?: "current-password" | "new-password";
31
+ /** The show toggle's name, "Show password" when not given: give each of two fields in a
32
+ * form its own, such as "Show password again". */
33
+ toggleLabel?: string;
29
34
  }>;
30
35
  /** A boxed password field with a show toggle inside its box, built on `@foldkit/ui`'s Input:
31
- * named by its label, described by its help line, `aria-invalid` when it failed and
32
- * `aria-required` when the form is not sent without it. The toggle is a button named "Show
33
- * password", `aria-pressed` while the password shows. For a new password, its strength is a
34
- * meter. */
36
+ * named by its label, described by its help line, `aria-invalid` and described by its
37
+ * `error` when it failed, and `aria-required` when the form is not sent without it. The
38
+ * toggle is a button named `toggleLabel` ("Show password"), `aria-pressed` while the
39
+ * password shows. For a new password, its strength is a meter. */
35
40
  export declare const view: <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>) => Html;
36
41
  //# sourceMappingURL=passwordField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"passwordField.d.ts","sourceRoot":"","sources":["../../../src/passwordField/passwordField.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAQtD,wEAAwE;AACxE,eAAO,MAAM,QAAQ,2CAAmC,CAAC;AACzD,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAiB5C,gEAAgE;AAChE,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC;gDAC4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,aAAa,EAAE,OAAO,CAAC;IACvB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;iDAC6C;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gEAAgE;IAChE,YAAY,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAAC;CACpD,CAAC,CAAC;AAEH;;;;aAIa;AACb,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IA8EpF,CAAC"}
1
+ {"version":3,"file":"passwordField.d.ts","sourceRoot":"","sources":["../../../src/passwordField/passwordField.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAStD,wEAAwE;AACxE,eAAO,MAAM,QAAQ,2CAAmC,CAAC;AACzD,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAiB5C,gEAAgE;AAChE,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC;gDAC4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,aAAa,EAAE,OAAO,CAAC;IACvB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;uCAEmC;IACnC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;iDAC6C;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gEAAgE;IAChE,YAAY,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAAC;IACnD;uDACmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC,CAAC;AAEH;;;;mEAImE;AACnE,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAiFpF,CAAC"}
@@ -2,6 +2,7 @@ import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
2
  import { Input as UiInput } from "@foldkit/ui";
3
3
  import { Match, Schema } from "effect";
4
4
  import { pressedAttribute } from "../button/button.js";
5
+ import { describedByError, errorView } from "../field/field.js";
5
6
  import * as IconButton from "../iconButton/iconButton.js";
6
7
  import * as Meter from "../meter/meter.js";
7
8
  // STRENGTH
@@ -10,17 +11,17 @@ export const Strength = Schema.Literals([0, 1, 2, 3, 4]);
10
11
  const STRENGTH_CELLS = 4;
11
12
  const strengthWord = (strength) => __foldkitBrandViewResult((Match.value(strength).pipe(Match.withReturnType(), Match.when(0, () => __foldkitBrandViewResult(("too short"), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#anonymous")), Match.when(1, () => __foldkitBrandViewResult(("weak"), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#anonymous~2")), Match.when(2, () => __foldkitBrandViewResult(("fair"), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#anonymous~3")), Match.when(3, () => __foldkitBrandViewResult(("good"), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#anonymous~4")), Match.when(4, () => __foldkitBrandViewResult(("strong"), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#anonymous~5")), Match.exhaustive)), "@gjermundgaraba-designs/pixel-paper/src/passwordField/passwordField.js#strengthWord");
12
13
  /** A boxed password field with a show toggle inside its box, built on `@foldkit/ui`'s Input:
13
- * named by its label, described by its help line, `aria-invalid` when it failed and
14
- * `aria-required` when the form is not sent without it. The toggle is a button named "Show
15
- * password", `aria-pressed` while the password shows. For a new password, its strength is a
16
- * meter. */
14
+ * named by its label, described by its help line, `aria-invalid` and described by its
15
+ * `error` when it failed, and `aria-required` when the form is not sent without it. The
16
+ * toggle is a button named `toggleLabel` ("Show password"), `aria-pressed` while the
17
+ * password shows. For a new password, its strength is a meter. */
17
18
  export const view = (config, h) => {
18
- const { id, label, value, onInput, isShown, onToggleShown, help, isInvalid = false, isRequired = false, strength, autocomplete = "current-password", } = config;
19
+ const { id, label, value, onInput, isShown, onToggleShown, help, error, isRequired = false, strength, autocomplete = "current-password", toggleLabel = "Show password", } = config;
19
20
  return __foldkitBrandViewResult((UiInput.view({
20
21
  id,
21
22
  value,
22
23
  onInput,
23
- isInvalid,
24
+ isInvalid: error !== undefined,
24
25
  type: isShown ? "text" : "password",
25
26
  hasDescription: help !== undefined,
26
27
  toView: (groups) => __foldkitBrandViewResult((h.div([h.Class("field boxed")], [
@@ -30,16 +31,18 @@ export const view = (config, h) => {
30
31
  h.input([
31
32
  ...groups.input,
32
33
  h.Autocomplete(autocomplete),
34
+ ...describedByError(id, error, help !== undefined, h),
33
35
  ...(isRequired ? [h.AriaRequired(true)] : []),
34
36
  ]),
35
37
  IconButton.view({
36
38
  glyph: isShown ? "eye-off" : "eye",
37
- label: "Show password",
39
+ label: toggleLabel,
38
40
  isQuiet: true,
39
41
  onClick: onToggleShown,
40
42
  attributes: [pressedAttribute(isShown, h)],
41
43
  }, h),
42
44
  ]),
45
+ ...errorView(id, error, h),
43
46
  ...(strength === undefined
44
47
  ? []
45
48
  : [
@@ -83,8 +83,8 @@ export type ViewInputs = Readonly<{
83
83
  options: ReadonlyArray<RadioOption>;
84
84
  /** The parent's selection. */
85
85
  selectedValue: Option.Option<string>;
86
- /** The group is `aria-invalid` and its marks and names turn red; say why in an
87
- * ErrorSummary above the form. */
86
+ /** The group is `aria-invalid` and its marks and names turn red; say why in its
87
+ * Fieldset's `error` and in an ErrorSummary above the form. */
88
88
  isInvalid?: boolean;
89
89
  }>;
90
90
  /**
@@ -117,8 +117,8 @@ export declare const view: import("foldkit/submodel").View<{
117
117
  options: ReadonlyArray<RadioOption>;
118
118
  /** The parent's selection. */
119
119
  selectedValue: Option.Option<string>;
120
- /** The group is `aria-invalid` and its marks and names turn red; say why in an
121
- * ErrorSummary above the form. */
120
+ /** The group is `aria-invalid` and its marks and names turn red; say why in its
121
+ * Fieldset's `error` and in an ErrorSummary above the form. */
122
122
  isInvalid?: boolean;
123
123
  }>>;
124
124
  //# sourceMappingURL=radioChoices.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"radioChoices.d.ts","sourceRoot":"","sources":["../../../src/radioChoices/radioChoices.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK/C,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAIzC;2FAC2F;AAC3F,eAAO,MAAM,KAAK;;;;;EAAoD,CAAC;AACvE,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,wDAAwD;AACxD,eAAO,MAAM,OAAO;;QACM,OAAO;;;;;;;;;;;EAC/B,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C;+EAC+E;AAC/E,eAAO,MAAM,UAAU;;QAAoC,KAAK;;EAAoB,CAAC;AACrF,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAIhD;gBACgB;AAChB,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,yEAAyE;AACzE,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAExC,CAAC;AAIH,qDAAqD;AACrD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAA6B,CAAC;AAIhE,+FAA+F;AAC/F,eAAO,MAAM,aAAa,UAAW,KAAK,KAAG,MAAiC,CAAC;AAiB/E,4FAA4F;AAC5F,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAIjD,CAAC;AAIL,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpF,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACpC,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrC;uCACmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;IAjBf,oFAAoF;WAC7E,MAAM;aACJ,aAAa,CAAC,WAAW,CAAC;IACnC,8BAA8B;mBACf,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IACpC;uCACmC;gBACvB,OAAO;GAkDnB,CAAC"}
1
+ {"version":3,"file":"radioChoices.d.ts","sourceRoot":"","sources":["../../../src/radioChoices/radioChoices.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK/C,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAIzC;2FAC2F;AAC3F,eAAO,MAAM,KAAK;;;;;EAAoD,CAAC;AACvE,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,wDAAwD;AACxD,eAAO,MAAM,OAAO;;QACM,OAAO;;;;;;;;;;;EAC/B,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C;+EAC+E;AAC/E,eAAO,MAAM,UAAU;;QAAoC,KAAK;;EAAoB,CAAC;AACrF,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAIhD;gBACgB;AAChB,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,yEAAyE;AACzE,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAExC,CAAC;AAIH,qDAAqD;AACrD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAA6B,CAAC;AAIhE,+FAA+F;AAC/F,eAAO,MAAM,aAAa,UAAW,KAAK,KAAG,MAAiC,CAAC;AAiB/E,4FAA4F;AAC5F,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAIjD,CAAC;AAIL,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpF,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACpC,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrC;oEACgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;IAjBf,oFAAoF;WAC7E,MAAM;aACJ,aAAa,CAAC,WAAW,CAAC;IACnC,8BAA8B;mBACf,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IACpC;oEACgE;gBACpD,OAAO;GAkDnB,CAAC"}
@@ -113,8 +113,8 @@ export type ViewInputs = Readonly<{
113
113
  }>;
114
114
  /**
115
115
  * A column of panels beside the deck, fixed under the top bar and scrolling itself:
116
- * `div.railed > aside.rail[popover="auto"][aria-label]` and the deck. One element serves both
117
- * layouts, so nothing in it renders twice or loses its state. At 900px and under it leaves the
116
+ * `div.railed >` the deck, then `aside.rail[popover="auto"][aria-label]`. One element serves
117
+ * both layouts, so nothing in it renders twice or loses its state. At 900px and under it leaves the
118
118
  * page, and `span.rail-open > button.btn.small` at the top of the deck opens the same element as a
119
119
  * drawer over a veil, its head (the name and a close glyph) showing. Opening focuses its first
120
120
  * control; Escape, a press on the veil, the close glyph, focus leaving or the window growing
@@ -1 +1 @@
1
- {"version":3,"file":"rail.d.ts","sourceRoot":"","sources":["../../../src/rail/rail.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rail.d.ts","sourceRoot":"","sources":["../../../src/rail/rail.ts"],"names":[],"mappings":"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"}
@@ -134,8 +134,8 @@ export const FollowRailDrawer = Mount.defineStream("FollowRailDrawer", {
134
134
  });
135
135
  /**
136
136
  * A column of panels beside the deck, fixed under the top bar and scrolling itself:
137
- * `div.railed > aside.rail[popover="auto"][aria-label]` and the deck. One element serves both
138
- * layouts, so nothing in it renders twice or loses its state. At 900px and under it leaves the
137
+ * `div.railed >` the deck, then `aside.rail[popover="auto"][aria-label]`. One element serves
138
+ * both layouts, so nothing in it renders twice or loses its state. At 900px and under it leaves the
139
139
  * page, and `span.rail-open > button.btn.small` at the top of the deck opens the same element as a
140
140
  * drawer over a veil, its head (the name and a close glyph) showing. Opening focuses its first
141
141
  * control; Escape, a press on the veil, the close glyph, focus leaving or the window growing
@@ -160,7 +160,10 @@ export const view = defineView((model, viewInputs, h) => {
160
160
  ],
161
161
  }, h),
162
162
  ]);
163
+ // NOTE: the deck comes first so the page's main content, and its h1, precede the rail's
164
+ // panels in reading order; the stylesheet places the rail, fixed, at the left.
163
165
  return __foldkitBrandViewResult((h.div([h.Class("railed")], [
166
+ toDeckView(openButton),
164
167
  h.aside([
165
168
  h.Id(model.id),
166
169
  h.Popover("auto"),
@@ -174,6 +177,5 @@ export const view = defineView((model, viewInputs, h) => {
174
177
  ]),
175
178
  ...toAsideView(),
176
179
  ]),
177
- toDeckView(openButton),
178
180
  ])), "@gjermundgaraba-designs/pixel-paper/src/rail/rail.js#anonymous~10");
179
181
  });
@@ -36,6 +36,10 @@ export type RowConfig<Message> = Readonly<{
36
36
  body: ReadonlyArray<Html | string>;
37
37
  /** Right-aligned chips, marks, counts, due days. */
38
38
  slot?: ReadonlyArray<Html | string>;
39
+ /** Further attributes for the slot's `span.slot`, such as an id the row's line is described
40
+ * by (`aria-describedby`); there is no span, and so nothing to point at, while `slot` is
41
+ * empty. Never a class. */
42
+ slotAttributes?: Attributes<Message>;
39
43
  /** Controls that belong to the row's job, before the tools (an approve and reject pair). */
40
44
  actions?: ReadonlyArray<Html | string>;
41
45
  /** Icon buttons at the row's right end, dimmed until the row is hovered or focused (full
@@ -67,12 +71,13 @@ export declare const glyphView: <Message>(config: GlyphConfig, h: HtmlBuilder<Me
67
71
  /** Configuration for rendering a fold toggle with `toggleView`. */
68
72
  export type ToggleConfig<Message> = Readonly<{
69
73
  isExpanded: boolean;
70
- /** What folds: `"Fold Notes"` and `"Unfold Notes"` are built from it. */
74
+ /** What the nested rows hang under: the toggle is named `"Rows under Notes"` whether open
75
+ * or not, and `aria-expanded` says which. */
71
76
  label: string;
72
77
  onToggle: Message;
73
78
  }>;
74
79
  /** The glyph slot as a fold toggle, built on `@foldkit/ui`'s Button: a ring, open or closed,
75
- * `aria-expanded`, named for what it does next. */
80
+ * `aria-expanded`, named for the rows it shows. */
76
81
  export declare const toggleView: <Message>(config: ToggleConfig<Message>, h: HtmlBuilder<Message>) => Html;
77
82
  /** Configuration for rendering a row's title with `titleView`. */
78
83
  export type TitleConfig<Message> = Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../../src/rows/rows.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../../src/rows/rows.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@ const lineView = (config, box, text, h) => __foldkitBrandViewResult((h.div([...(
15
15
  ...box,
16
16
  ...(config.glyph === undefined ? [] : [config.glyph]),
17
17
  text,
18
- ...slotView("span", "slot", config.slot ?? [], h),
18
+ ...slotView("span", "slot", config.slot ?? [], h, config.slotAttributes),
19
19
  ...(config.actions ?? []),
20
20
  ...slotView("span", "tools", config.tools ?? [], h),
21
21
  ])), "@gjermundgaraba-designs/pixel-paper/src/rows/rows.js#lineView");
@@ -42,13 +42,13 @@ export const rowView = (config, h) => __foldkitBrandViewResult((h.keyed("li")(co
42
42
  /** A glyph in the row's 18px slot. */
43
43
  export const glyphView = (config, h) => __foldkitBrandViewResult((h.span([h.Class("glyph")], [Glyph.view({ name: config.name }, h)])), "@gjermundgaraba-designs/pixel-paper/src/rows/rows.js#glyphView");
44
44
  /** The glyph slot as a fold toggle, built on `@foldkit/ui`'s Button: a ring, open or closed,
45
- * `aria-expanded`, named for what it does next. */
45
+ * `aria-expanded`, named for the rows it shows. */
46
46
  export const toggleView = (config, h) => __foldkitBrandViewResult((UiButton.view({
47
47
  onClick: config.onToggle,
48
48
  toView: (groups) => __foldkitBrandViewResult((h.button([
49
49
  ...groups.button,
50
50
  h.AriaExpanded(config.isExpanded),
51
- h.AriaLabel(`${config.isExpanded ? "Fold" : "Unfold"} ${config.label}`),
51
+ h.AriaLabel(`Rows under ${config.label}`),
52
52
  h.Class("glyph"),
53
53
  ], [Glyph.view({ name: config.isExpanded ? "ring" : "ring-closed" }, h)])), "@gjermundgaraba-designs/pixel-paper/src/rows/rows.js#toView~2"),
54
54
  }, h)), "@gjermundgaraba-designs/pixel-paper/src/rows/rows.js#toggleView");
@@ -24,14 +24,16 @@ export type ViewConfig<Message> = Readonly<{
24
24
  options: ReadonlyArray<SelectOption | SelectGroup>;
25
25
  /** A sans sentence under the control. */
26
26
  help?: string;
27
- /** Red on the control's box and on its label; say why in an ErrorSummary above the form. */
28
- isInvalid?: boolean;
27
+ /** Why the value was refused: a red sentence under the control, which the control is
28
+ * described by, with red on its box and its label (`aria-invalid`). Say it again in an
29
+ * ErrorSummary above the form. */
30
+ error?: string | undefined;
29
31
  /** The form is not sent without it: screen readers hear "required" (`aria-required`). */
30
32
  isRequired?: boolean;
31
33
  }>;
32
34
  /** A labelled select with its help line, built on `@foldkit/ui`'s `Select`: the label names
33
- * the select, the help line describes it (`aria-describedby`), `isInvalid` sets
34
- * `aria-invalid` and `isRequired` sets `aria-required`. The stylesheet draws the box, the
35
- * caret and the picker. */
35
+ * the select, the help line describes it (`aria-describedby`), an `error` sets
36
+ * `aria-invalid` and describes it before the help, and `isRequired` sets `aria-required`.
37
+ * The stylesheet draws the box, the caret and the picker. */
36
38
  export declare const view: <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>) => Html;
37
39
  //# sourceMappingURL=selectField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"selectField.d.ts","sourceRoot":"","sources":["../../../src/selectField/selectField.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAItD,qCAAqC;AACrC,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAEH,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACtC,CAAC,CAAC;AAEH,8DAA8D;AAC9D,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,sEAAsE;IACtE,OAAO,EAAE,aAAa,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC;IACnD,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAoBH;;;4BAG4B;AAC5B,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAqCpF,CAAC"}
1
+ {"version":3,"file":"selectField.d.ts","sourceRoot":"","sources":["../../../src/selectField/selectField.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMtD,qCAAqC;AACrC,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAEH,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACtC,CAAC,CAAC;AAEH,8DAA8D;AAC9D,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACrC,sEAAsE;IACtE,OAAO,EAAE,aAAa,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC;IACnD,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;uCAEmC;IACnC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAoBH;;;8DAG8D;AAC9D,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAiCpF,CAAC"}
@@ -1,27 +1,33 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
2
  import { Select as UiSelect } from "@foldkit/ui";
3
3
  import { Array } from "effect";
4
+ import { describedByError, errorView } from "../field/field.js";
4
5
  const isGroup = (entry) => __foldkitBrandViewResult(("options" in entry), "@gjermundgaraba-designs/pixel-paper/src/selectField/selectField.js#isGroup");
5
6
  const optionView = (h) => __foldkitBrandViewResult((({ value, label, isDisabled = false }) => __foldkitBrandViewResult((h.keyed("option")(value, [h.Value(value), h.Disabled(isDisabled)], [label])), "@gjermundgaraba-designs/pixel-paper/src/selectField/selectField.js#anonymous")), "@gjermundgaraba-designs/pixel-paper/src/selectField/selectField.js#optionView");
6
7
  const entryView = (h) => __foldkitBrandViewResult(((entry) => __foldkitBrandViewResult((isGroup(entry)
7
8
  ? h.keyed("optgroup")(entry.key, [h.Attribute("label", entry.label)], Array.map(entry.options, optionView(h)))
8
9
  : optionView(h)(entry)), "@gjermundgaraba-designs/pixel-paper/src/selectField/selectField.js#anonymous~2")), "@gjermundgaraba-designs/pixel-paper/src/selectField/selectField.js#entryView");
9
10
  /** A labelled select with its help line, built on `@foldkit/ui`'s `Select`: the label names
10
- * the select, the help line describes it (`aria-describedby`), `isInvalid` sets
11
- * `aria-invalid` and `isRequired` sets `aria-required`. The stylesheet draws the box, the
12
- * caret and the picker. */
11
+ * the select, the help line describes it (`aria-describedby`), an `error` sets
12
+ * `aria-invalid` and describes it before the help, and `isRequired` sets `aria-required`.
13
+ * The stylesheet draws the box, the caret and the picker. */
13
14
  export const view = (config, h) => {
14
- const { id, label, value, onChange, options, help, isInvalid = false, isRequired = false, } = config;
15
+ const { id, label, value, onChange, options, help, error, isRequired = false } = config;
15
16
  return __foldkitBrandViewResult((UiSelect.view({
16
17
  id,
17
18
  value,
18
19
  onChange,
19
- isInvalid,
20
+ isInvalid: error !== undefined,
20
21
  hasDescription: help !== undefined,
21
22
  toView: (groups) => __foldkitBrandViewResult((h.div([h.Class("field")], [
22
23
  h.label(groups.label, [label]),
23
24
  // NOTE: @foldkit/ui gap: Select has no required option, so the wrapper says it.
24
- h.select([...groups.select, ...(isRequired ? [h.AriaRequired(true)] : [])], Array.map(options, entryView(h))),
25
+ h.select([
26
+ ...groups.select,
27
+ ...describedByError(id, error, help !== undefined, h),
28
+ ...(isRequired ? [h.AriaRequired(true)] : []),
29
+ ], Array.map(options, entryView(h))),
30
+ ...errorView(id, error, h),
25
31
  ...(help === undefined
26
32
  ? []
27
33
  : [h.p([...groups.description, h.Class("field-help")], [help])]),
@@ -10,7 +10,8 @@ export type ViewConfig = Readonly<{
10
10
  /** A Prose, and anything under it. */
11
11
  body: ReadonlyArray<Html | string>;
12
12
  }>;
13
- /** An authored page inside a panel, in an `<article>` under its `h1`: the one place the
14
- * system slows to reading pace. */
13
+ /** An authored page, an `<article>` under its `h1` on its own sheet of paper, framed as a
14
+ * panel is: the one place the system slows to reading pace. It stands in a panel's place,
15
+ * never inside one, so its `h1` opens the page instead of following a panel's `h2`. */
15
16
  export declare const view: <Message>(config: ViewConfig, h: HtmlBuilder<Message>) => Html;
16
17
  //# sourceMappingURL=sheet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../src/sheet/sheet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMtD,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACtC,mDAAmD;IACnD,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACvC,sCAAsC;IACtC,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;CACpC,CAAC,CAAC;AAEH;oCACoC;AACpC,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAc3E,CAAC"}
1
+ {"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../src/sheet/sheet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMtD,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACtC,mDAAmD;IACnD,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACvC,sCAAsC;IACtC,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;CACpC,CAAC,CAAC;AAEH;;wFAEwF;AACxF,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAc3E,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
2
  import { slotView } from "../slot/slot.js";
3
- /** An authored page inside a panel, in an `<article>` under its `h1`: the one place the
4
- * system slows to reading pace. */
3
+ /** An authored page, an `<article>` under its `h1` on its own sheet of paper, framed as a
4
+ * panel is: the one place the system slows to reading pace. It stands in a panel's place,
5
+ * never inside one, so its `h1` opens the page instead of following a panel's `h2`. */
5
6
  export const view = (config, h) => {
6
7
  const { title, byline = [], actions = [], body } = config;
7
8
  return __foldkitBrandViewResult((h.article([h.Class("sheet")], [
@@ -1,8 +1,9 @@
1
1
  import type { Html, HtmlBuilder } from "foldkit/html";
2
+ import type { Attributes } from "../attributes/attributes.ts";
2
3
  /** The elements a slot is wrapped in. */
3
4
  type SlotTag = "div" | "span" | "p" | "dd" | "ul";
4
5
  /** An optional slot in its one element, `tag.slotClass`, to spread among its siblings: nothing
5
6
  * while the slot is empty, so an unused slot leaves no box, gap or rule behind. */
6
- export declare const slotView: <Message>(tag: SlotTag, slotClass: string, slot: ReadonlyArray<Html | string>, h: HtmlBuilder<Message>) => ReadonlyArray<Html>;
7
+ export declare const slotView: <Message>(tag: SlotTag, slotClass: string, slot: ReadonlyArray<Html | string>, h: HtmlBuilder<Message>, attributes?: Attributes<Message>) => ReadonlyArray<Html>;
7
8
  export {};
8
9
  //# sourceMappingURL=slot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../../../src/slot/slot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,yCAAyC;AACzC,KAAK,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD;oFACoF;AACpF,eAAO,MAAM,QAAQ,GAAI,OAAO,OACzB,OAAO,aACD,MAAM,QACX,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,KAC/B,WAAW,CAAC,OAAO,CAAC,KACtB,aAAa,CAAC,IAAI,CAIjB,CAAC"}
1
+ {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../../../src/slot/slot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,yCAAyC;AACzC,KAAK,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD;oFACoF;AACpF,eAAO,MAAM,QAAQ,GAAI,OAAO,OACzB,OAAO,aACD,MAAM,QACX,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,KAC/B,WAAW,CAAC,OAAO,CAAC,eACX,UAAU,CAAC,OAAO,CAAC,KAC9B,aAAa,CAAC,IAAI,CAIjB,CAAC"}
@@ -2,7 +2,7 @@ import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
2
  import { Array } from "effect";
3
3
  /** An optional slot in its one element, `tag.slotClass`, to spread among its siblings: nothing
4
4
  * while the slot is empty, so an unused slot leaves no box, gap or rule behind. */
5
- export const slotView = (tag, slotClass, slot, h) => __foldkitBrandViewResult((Array.match(slot, {
5
+ export const slotView = (tag, slotClass, slot, h, attributes = []) => __foldkitBrandViewResult((Array.match(slot, {
6
6
  onEmpty: () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/slot/slot.js#onEmpty"),
7
- onNonEmpty: (content) => __foldkitBrandViewResult(([h[tag]([h.Class(slotClass)], content)]), "@gjermundgaraba-designs/pixel-paper/src/slot/slot.js#onNonEmpty"),
7
+ onNonEmpty: (content) => __foldkitBrandViewResult(([h[tag]([...attributes, h.Class(slotClass)], content)]), "@gjermundgaraba-designs/pixel-paper/src/slot/slot.js#onNonEmpty"),
8
8
  })), "@gjermundgaraba-designs/pixel-paper/src/slot/slot.js#slotView");
@@ -1,4 +1,4 @@
1
- import type { Html, HtmlBuilder } from "foldkit/html";
1
+ import type { ChildAttribute, Html, HtmlBuilder, TextareaAttribute } from "foldkit/html";
2
2
  /** Configuration for rendering a textarea field with `view`. */
3
3
  export type ViewConfig<Message> = Readonly<{
4
4
  id: string;
@@ -7,13 +7,23 @@ export type ViewConfig<Message> = Readonly<{
7
7
  onInput: (value: string) => Message;
8
8
  /** A sans sentence under the control. */
9
9
  help?: string;
10
- /** Red on the control's rule and on its label; say why in an ErrorSummary above the form. */
11
- isInvalid?: boolean;
10
+ /** Why the value was refused: a red sentence under the control, which the control is
11
+ * described by, with red on its rule and its label (`aria-invalid`). Say it again in an
12
+ * ErrorSummary above the form. */
13
+ error?: string | undefined;
14
+ /** The form is not sent without it: screen readers hear "required" (`aria-required`). */
15
+ isRequired?: boolean;
16
+ /** Code and identifiers: the system monospace. */
17
+ isMono?: boolean;
12
18
  /** The lines it opens at; it grows with its text from there. */
13
19
  rows?: number;
20
+ /** Further attributes for the `<textarea>`, such as a placeholder or spelling off; never a
21
+ * class, and never its text, which is `value`. */
22
+ textareaAttributes?: ReadonlyArray<TextareaAttribute<Message> | ChildAttribute>;
14
23
  }>;
15
24
  /** A labelled textarea with its help line, built on `@foldkit/ui`'s `Textarea`: the label
16
- * names the textarea, the help line describes it (`aria-describedby`) and `isInvalid` sets
17
- * `aria-invalid`. Boxless like a Field, on a dotted baseline, with an ink corner grip. */
25
+ * names the textarea, the help line describes it (`aria-describedby`), an `error` sets
26
+ * `aria-invalid` and describes it before the help, and `isRequired` sets `aria-required`.
27
+ * Boxless like a Field, on a dotted baseline, with an ink corner grip. */
18
28
  export declare const view: <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>) => Html;
19
29
  //# sourceMappingURL=textareaField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"textareaField.d.ts","sourceRoot":"","sources":["../../../src/textareaField/textareaField.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAItD,gEAAgE;AAChE,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH;;2FAE2F;AAC3F,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAyBpF,CAAC"}
1
+ {"version":3,"file":"textareaField.d.ts","sourceRoot":"","sources":["../../../src/textareaField/textareaField.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAMzF,gEAAgE;AAChE,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;uCAEmC;IACnC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,kBAAkB,CAAC,EAAE,aAAa,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;CACjF,CAAC,CAAC;AAEH;;;2EAG2E;AAC3E,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IA2CpF,CAAC"}
@@ -1,20 +1,29 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
2
  import { Textarea as UiTextarea } from "@foldkit/ui";
3
+ import { describedByError, errorView } from "../field/field.js";
3
4
  /** A labelled textarea with its help line, built on `@foldkit/ui`'s `Textarea`: the label
4
- * names the textarea, the help line describes it (`aria-describedby`) and `isInvalid` sets
5
- * `aria-invalid`. Boxless like a Field, on a dotted baseline, with an ink corner grip. */
5
+ * names the textarea, the help line describes it (`aria-describedby`), an `error` sets
6
+ * `aria-invalid` and describes it before the help, and `isRequired` sets `aria-required`.
7
+ * Boxless like a Field, on a dotted baseline, with an ink corner grip. */
6
8
  export const view = (config, h) => {
7
- const { id, label, value, onInput, help, isInvalid = false, rows } = config;
9
+ const { id, label, value, onInput, help, error, isRequired = false, isMono = false, rows, textareaAttributes = [], } = config;
8
10
  return __foldkitBrandViewResult((UiTextarea.view({
9
11
  id,
10
12
  value,
11
13
  onInput,
12
- isInvalid,
14
+ isInvalid: error !== undefined,
13
15
  hasDescription: help !== undefined,
14
16
  ...(rows !== undefined && { rows }),
15
- toView: (groups) => __foldkitBrandViewResult((h.div([h.Class("field")], [
17
+ toView: (groups) => __foldkitBrandViewResult((h.div([h.Class(`field${isMono ? " mono" : ""}`)], [
16
18
  h.label(groups.label, [label]),
17
- h.textarea(groups.textarea),
19
+ // NOTE: @foldkit/ui gap: Textarea has no required option, so the wrapper says it.
20
+ h.textarea([
21
+ ...textareaAttributes,
22
+ ...groups.textarea,
23
+ ...describedByError(id, error, help !== undefined, h),
24
+ ...(isRequired ? [h.AriaRequired(true)] : []),
25
+ ]),
26
+ ...errorView(id, error, h),
18
27
  ...(help === undefined
19
28
  ? []
20
29
  : [h.p([...groups.description, h.Class("field-help")], [help])]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba-designs/pixel-paper",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,6 +7,10 @@ import type { Html, HtmlBuilder } from "foldkit/html";
7
7
  export type ViewConfig = Readonly<{
8
8
  /** Pixel caps: what is missing (`"Nothing queued"`). */
9
9
  title: string;
10
+ /** Makes the title a heading at this level, for an empty state that is the page's or the
11
+ * section's main content (a gate, a missing page); a `strong` when not given. It looks the
12
+ * same either way. */
13
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
10
14
  /** One sentence: how it gets filled. */
11
15
  body?: ReadonlyArray<Html | string>;
12
16
  /** What fills it: a button under the sentence. */
@@ -15,12 +19,14 @@ export type ViewConfig = Readonly<{
15
19
 
16
20
  /** What a panel says when it has nothing to show, centred where its content would be. */
17
21
  export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
18
- const { title, body = [], actions = [] } = config;
22
+ const { title, headingLevel, body = [], actions = [] } = config;
19
23
 
20
24
  return h.div(
21
25
  [h.Class("empty")],
22
26
  [
23
- h.strong([], [title]),
27
+ headingLevel === undefined
28
+ ? h.strong([h.Class("empty-title")], [title])
29
+ : h[`h${headingLevel}`]([h.Class("empty-title")], [title]),
24
30
  ...Array.match(body, { onEmpty: () => [], onNonEmpty: (sentence) => [h.p([], sentence)] }),
25
31
  ...actions,
26
32
  ],