@gjermundgaraba-designs/simple-rat 0.7.0 → 0.8.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 (159) hide show
  1. package/DESIGN.md +5 -5
  2. package/dist/highlight/highlight.d.ts.map +1 -1
  3. package/dist/highlight/highlight.js +66 -115
  4. package/dist/highlight/vite.d.ts +2 -1
  5. package/dist/highlight/vite.d.ts.map +1 -1
  6. package/dist/highlight/vite.js +5 -6
  7. package/dist/src/code/code.d.ts +4 -4
  8. package/dist/src/code/code.d.ts.map +1 -1
  9. package/dist/src/code/code.js +10 -7
  10. package/dist/src/code/index.d.ts.map +1 -1
  11. package/dist/src/{codeBlock/codeBlock.d.ts → code-block/code-block.d.ts} +2 -3
  12. package/dist/src/code-block/code-block.d.ts.map +1 -0
  13. package/dist/src/{codeBlock/codeBlock.js → code-block/code-block.js} +13 -7
  14. package/dist/src/code-block/index.d.ts +3 -0
  15. package/dist/src/code-block/index.d.ts.map +1 -0
  16. package/dist/src/code-block/index.js +1 -0
  17. package/dist/src/copy/copy.d.ts.map +1 -1
  18. package/dist/src/copy/copy.js +1 -7
  19. package/dist/src/{copyTab/copyTab.d.ts → copy-tab/copy-tab.d.ts} +3 -2
  20. package/dist/src/copy-tab/copy-tab.d.ts.map +1 -0
  21. package/dist/src/{copyTab/copyTab.js → copy-tab/copy-tab.js} +13 -13
  22. package/dist/src/copy-tab/index.d.ts +3 -0
  23. package/dist/src/copy-tab/index.d.ts.map +1 -0
  24. package/dist/src/{copyTab → copy-tab}/index.js +1 -1
  25. package/dist/src/diagram/diagram.d.ts.map +1 -1
  26. package/dist/src/diagram/index.d.ts.map +1 -1
  27. package/dist/src/footer/footer.d.ts.map +1 -1
  28. package/dist/src/footer/index.d.ts.map +1 -1
  29. package/dist/src/footnotes/footnotes.d.ts.map +1 -1
  30. package/dist/src/footnotes/footnotes.js +7 -2
  31. package/dist/src/footnotes/index.d.ts.map +1 -1
  32. package/dist/src/header/header.d.ts +1 -1
  33. package/dist/src/header/header.d.ts.map +1 -1
  34. package/dist/src/header/index.d.ts.map +1 -1
  35. package/dist/src/heading/heading.d.ts.map +1 -1
  36. package/dist/src/heading/index.d.ts.map +1 -1
  37. package/dist/src/index.d.ts +6 -6
  38. package/dist/src/index.d.ts.map +1 -1
  39. package/dist/src/index.js +6 -8
  40. package/dist/src/nav-link/index.d.ts +3 -0
  41. package/dist/src/nav-link/index.d.ts.map +1 -0
  42. package/dist/src/nav-link/index.js +1 -0
  43. package/dist/src/{navLink/navLink.d.ts → nav-link/nav-link.d.ts} +1 -1
  44. package/dist/src/nav-link/nav-link.d.ts.map +1 -0
  45. package/dist/src/{navLink/navLink.js → nav-link/nav-link.js} +1 -1
  46. package/dist/src/note/index.d.ts.map +1 -1
  47. package/dist/src/note/note.d.ts.map +1 -1
  48. package/dist/src/pager/index.d.ts.map +1 -1
  49. package/dist/src/pager/pager.d.ts.map +1 -1
  50. package/dist/src/post-list/index.d.ts +3 -0
  51. package/dist/src/post-list/index.d.ts.map +1 -0
  52. package/dist/src/post-list/index.js +1 -0
  53. package/dist/src/{postList/postList.d.ts → post-list/post-list.d.ts} +3 -2
  54. package/dist/src/post-list/post-list.d.ts.map +1 -0
  55. package/dist/src/post-list/post-list.js +6 -0
  56. package/dist/src/post-meta/index.d.ts +3 -0
  57. package/dist/src/post-meta/index.d.ts.map +1 -0
  58. package/dist/src/post-meta/index.js +1 -0
  59. package/dist/src/{postMeta/postMeta.d.ts → post-meta/post-meta.d.ts} +1 -1
  60. package/dist/src/post-meta/post-meta.d.ts.map +1 -0
  61. package/dist/src/{postMeta/postMeta.js → post-meta/post-meta.js} +3 -3
  62. package/dist/src/scheme/index.d.ts +1 -1
  63. package/dist/src/scheme/index.d.ts.map +1 -1
  64. package/dist/src/scheme/index.js +1 -1
  65. package/dist/src/scheme/{schemeSwitch.d.ts → scheme-switch.d.ts} +3 -2
  66. package/dist/src/scheme/scheme-switch.d.ts.map +1 -0
  67. package/dist/src/scheme/{schemeSwitch.js → scheme-switch.js} +7 -5
  68. package/dist/src/scheme/scheme.d.ts +0 -2
  69. package/dist/src/scheme/scheme.d.ts.map +1 -1
  70. package/dist/src/scheme/scheme.js +0 -2
  71. package/dist/src/scheme/switch.d.ts.map +1 -1
  72. package/dist/src/scheme/switch.js +12 -17
  73. package/dist/src/scrolls/scrolls.d.ts.map +1 -1
  74. package/dist/src/scrolls/scrolls.js +7 -9
  75. package/dist/src/skip-link/index.d.ts +2 -0
  76. package/dist/src/skip-link/index.d.ts.map +1 -0
  77. package/dist/src/skip-link/index.js +1 -0
  78. package/dist/src/{skipLink/skipLink.d.ts → skip-link/skip-link.d.ts} +1 -1
  79. package/dist/src/skip-link/skip-link.d.ts.map +1 -0
  80. package/dist/src/{skipLink/skipLink.js → skip-link/skip-link.js} +1 -1
  81. package/dist/src/table/index.d.ts.map +1 -1
  82. package/dist/src/table/table.d.ts +1 -1
  83. package/dist/src/table/table.d.ts.map +1 -1
  84. package/dist/src/table/table.js +3 -1
  85. package/highlight/highlight.ts +88 -118
  86. package/highlight/vite.ts +20 -10
  87. package/package.json +15 -16
  88. package/src/code/code.ts +18 -11
  89. package/src/code/index.ts +1 -0
  90. package/src/code/lines.d.ts +0 -5
  91. package/src/{codeBlock/codeBlock.ts → code-block/code-block.ts} +32 -14
  92. package/src/code-block/index.ts +3 -0
  93. package/src/copy/copy.ts +8 -17
  94. package/src/{copyTab/copyTab.ts → copy-tab/copy-tab.ts} +24 -8
  95. package/src/copy-tab/index.ts +12 -0
  96. package/src/diagram/diagram.ts +8 -4
  97. package/src/diagram/index.ts +1 -0
  98. package/src/footer/footer.ts +4 -2
  99. package/src/footer/index.ts +1 -0
  100. package/src/footnotes/footnotes.ts +26 -8
  101. package/src/footnotes/index.ts +1 -0
  102. package/src/header/header.ts +8 -5
  103. package/src/header/index.ts +1 -0
  104. package/src/heading/heading.ts +10 -2
  105. package/src/heading/index.ts +1 -0
  106. package/src/index.ts +23 -8
  107. package/src/nav-link/index.ts +3 -0
  108. package/src/{navLink/navLink.ts → nav-link/nav-link.ts} +14 -4
  109. package/src/note/index.ts +1 -0
  110. package/src/note/note.ts +6 -2
  111. package/src/pager/index.ts +1 -0
  112. package/src/pager/pager.ts +6 -2
  113. package/src/post-list/index.ts +3 -0
  114. package/src/post-list/post-list.ts +30 -0
  115. package/src/post-meta/index.ts +3 -0
  116. package/src/{postMeta/postMeta.ts → post-meta/post-meta.ts} +5 -2
  117. package/src/scheme/index.ts +3 -1
  118. package/src/scheme/{schemeSwitch.ts → scheme-switch.ts} +20 -8
  119. package/src/scheme/scheme.ts +3 -4
  120. package/src/scheme/switch.ts +9 -20
  121. package/src/scrolls/scrolls.ts +19 -13
  122. package/src/simple-rat.css +0 -47
  123. package/src/skip-link/index.ts +1 -0
  124. package/src/table/index.ts +1 -0
  125. package/src/table/table.ts +18 -9
  126. package/src/tokens.css +23 -32
  127. package/dist/src/codeBlock/codeBlock.d.ts.map +0 -1
  128. package/dist/src/codeBlock/index.d.ts +0 -3
  129. package/dist/src/codeBlock/index.d.ts.map +0 -1
  130. package/dist/src/codeBlock/index.js +0 -1
  131. package/dist/src/copyTab/copyTab.d.ts.map +0 -1
  132. package/dist/src/copyTab/index.d.ts +0 -3
  133. package/dist/src/copyTab/index.d.ts.map +0 -1
  134. package/dist/src/navLink/index.d.ts +0 -3
  135. package/dist/src/navLink/index.d.ts.map +0 -1
  136. package/dist/src/navLink/index.js +0 -1
  137. package/dist/src/navLink/navLink.d.ts.map +0 -1
  138. package/dist/src/postList/index.d.ts +0 -3
  139. package/dist/src/postList/index.d.ts.map +0 -1
  140. package/dist/src/postList/index.js +0 -1
  141. package/dist/src/postList/postList.d.ts.map +0 -1
  142. package/dist/src/postList/postList.js +0 -3
  143. package/dist/src/postMeta/index.d.ts +0 -3
  144. package/dist/src/postMeta/index.d.ts.map +0 -1
  145. package/dist/src/postMeta/index.js +0 -1
  146. package/dist/src/postMeta/postMeta.d.ts.map +0 -1
  147. package/dist/src/scheme/schemeSwitch.d.ts.map +0 -1
  148. package/dist/src/skipLink/index.d.ts +0 -2
  149. package/dist/src/skipLink/index.d.ts.map +0 -1
  150. package/dist/src/skipLink/index.js +0 -1
  151. package/dist/src/skipLink/skipLink.d.ts.map +0 -1
  152. package/src/codeBlock/index.ts +0 -2
  153. package/src/copyTab/index.ts +0 -2
  154. package/src/navLink/index.ts +0 -2
  155. package/src/postList/index.ts +0 -2
  156. package/src/postList/postList.ts +0 -19
  157. package/src/postMeta/index.ts +0 -2
  158. package/src/skipLink/index.ts +0 -1
  159. /package/src/{skipLink/skipLink.ts → skip-link/skip-link.ts} +0 -0
@@ -1,2 +1,3 @@
1
1
  export { view } from "./footer.ts";
2
+
2
3
  export type { ViewConfig } from "./footer.ts";
@@ -1,8 +1,19 @@
1
1
  import type { Html, HtmlBuilder } from "foldkit/html";
2
2
 
3
3
  /** A superscript number that jumps to the note numbered `noteNumber`; the note links back. */
4
- export const refView = <Message>(noteNumber: number, h: HtmlBuilder<Message>): Html =>
5
- h.sup([], [h.a([h.Href(`#fn-${noteNumber}`), h.Id(`ref-${noteNumber}`)], [`${noteNumber}`])]);
4
+ export const refView = <Message>(
5
+ noteNumber: number,
6
+ h: HtmlBuilder<Message>
7
+ ): Html =>
8
+ h.sup(
9
+ [],
10
+ [
11
+ h.a(
12
+ [h.Href(`#fn-${noteNumber}`), h.Id(`ref-${noteNumber}`)],
13
+ [`${noteNumber}`]
14
+ ),
15
+ ]
16
+ );
6
17
 
7
18
  /** Configuration for rendering the notes with `view`. */
8
19
  export type ViewConfig = Readonly<{
@@ -12,7 +23,10 @@ export type ViewConfig = Readonly<{
12
23
 
13
24
  /** The notes, numbered from 1, at the end of the page under a rule. Each ends in a `Back`
14
25
  link named for the reference it returns to. */
15
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html =>
26
+ export const view = <Message>(
27
+ config: ViewConfig,
28
+ h: HtmlBuilder<Message>
29
+ ): Html =>
16
30
  h.section(
17
31
  [h.Class("footnotes"), h.AriaLabel("Footnotes")],
18
32
  [
@@ -21,18 +35,22 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
21
35
  [],
22
36
  config.notes.map((note, index) => {
23
37
  const noteNumber = index + 1;
38
+
24
39
  return h.li(
25
40
  [h.Id(`fn-${noteNumber}`)],
26
41
  [
27
42
  ...note,
28
43
  " ",
29
44
  h.a(
30
- [h.Href(`#ref-${noteNumber}`), h.AriaLabel(`Back to reference ${noteNumber}`)],
31
- ["Back"],
45
+ [
46
+ h.Href(`#ref-${noteNumber}`),
47
+ h.AriaLabel(`Back to reference ${noteNumber}`),
48
+ ],
49
+ ["Back"]
32
50
  ),
33
- ],
51
+ ]
34
52
  );
35
- }),
53
+ })
36
54
  ),
37
- ],
55
+ ]
38
56
  );
@@ -1,2 +1,3 @@
1
1
  export { refView, view } from "./footnotes.ts";
2
+
2
3
  export type { ViewConfig } from "./footnotes.ts";
@@ -3,22 +3,25 @@ import type { Html, HtmlBuilder } from "foldkit/html";
3
3
  /** Configuration for rendering the header with `view`. */
4
4
  export type ViewConfig = Readonly<{
5
5
  /** `NavLink`s, and optionally a `SchemeSwitch` last. */
6
- items: ReadonlyArray<Html>;
6
+ items: readonly Html[];
7
7
  }>;
8
8
 
9
9
  /** The page's header: a nav named "Primary navigation", a line of lowercase links separated
10
10
  by middots, then a rule. Each item after the first sits in a `span` with its middot, so a
11
11
  wrapped line starts with the middot instead of ending on one. */
12
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html =>
12
+ export const view = <Message>(
13
+ config: ViewConfig,
14
+ h: HtmlBuilder<Message>
15
+ ): Html =>
13
16
  h.header(
14
17
  [],
15
18
  [
16
19
  h.nav(
17
20
  [h.AriaLabel("Primary navigation")],
18
21
  config.items.flatMap((item, index) =>
19
- index === 0 ? [item] : [" ", h.span([], ["· ", item])],
20
- ),
22
+ index === 0 ? [item] : [" ", h.span([], ["· ", item])]
23
+ )
21
24
  ),
22
25
  h.hr([]),
23
- ],
26
+ ]
24
27
  );
@@ -1,2 +1,3 @@
1
1
  export { view } from "./header.ts";
2
+
2
3
  export type { ViewConfig } from "./header.ts";
@@ -10,12 +10,20 @@ export type ViewConfig = Readonly<{
10
10
 
11
11
  /** A section heading with a `#` link to itself, shown on hover and focus. The page title is
12
12
  a plain `h1`. */
13
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
13
+ export const view = <Message>(
14
+ config: ViewConfig,
15
+ h: HtmlBuilder<Message>
16
+ ): Html => {
14
17
  const { level, id, text } = config;
18
+
15
19
  const children: ReadonlyArray<Html | string> = [
16
20
  text,
17
21
  " ",
18
- h.a([h.Class("self"), h.Href(`#${id}`), h.AriaLabel(`Link to ${text}`)], ["#"]),
22
+ h.a(
23
+ [h.Class("self"), h.Href(`#${id}`), h.AriaLabel(`Link to ${text}`)],
24
+ ["#"]
25
+ ),
19
26
  ];
27
+
20
28
  return level === 2 ? h.h2([h.Id(id)], children) : h.h3([h.Id(id)], children);
21
29
  };
@@ -1,2 +1,3 @@
1
1
  export { view } from "./heading.ts";
2
+
2
3
  export type { ViewConfig } from "./heading.ts";
package/src/index.ts CHANGED
@@ -1,20 +1,35 @@
1
- // simple rat's Foldkit components. Each renders the markup simple-rat.css styles; plain
2
- // elements (paragraphs, lists, quotes, kbd, mark, details) need no component.
3
1
  export * as Code from "./code/index.ts";
4
- export * as CodeBlock from "./codeBlock/index.ts";
5
- export * as CopyTab from "./copyTab/index.ts";
2
+
3
+ export * as CodeBlock from "./code-block/index.ts";
4
+
5
+ export * as CopyTab from "./copy-tab/index.ts";
6
+
6
7
  export * as Diagram from "./diagram/index.ts";
8
+
7
9
  export * as Footer from "./footer/index.ts";
10
+
8
11
  export * as Footnotes from "./footnotes/index.ts";
12
+
9
13
  export * as Header from "./header/index.ts";
14
+
10
15
  export * as Heading from "./heading/index.ts";
11
- export * as NavLink from "./navLink/index.ts";
16
+
17
+ export * as NavLink from "./nav-link/index.ts";
18
+
12
19
  export * as Note from "./note/index.ts";
20
+
13
21
  export * as Pager from "./pager/index.ts";
14
- export * as PostList from "./postList/index.ts";
15
- export * as PostMeta from "./postMeta/index.ts";
22
+
23
+ export * as PostList from "./post-list/index.ts";
24
+
25
+ export * as PostMeta from "./post-meta/index.ts";
26
+
16
27
  export * as Scheme from "./scheme/index.ts";
28
+
17
29
  export * as Scrolls from "./scrolls/index.ts";
18
- export * as SkipLink from "./skipLink/index.ts";
30
+
31
+ export * as SkipLink from "./skip-link/index.ts";
32
+
19
33
  export * as Swatch from "./swatch/index.ts";
34
+
20
35
  export * as Table from "./table/index.ts";
@@ -0,0 +1,3 @@
1
+ export { view } from "./nav-link.ts";
2
+
3
+ export type { ViewConfig } from "./nav-link.ts";
@@ -11,8 +11,18 @@ export type ViewConfig = Readonly<{
11
11
  }>;
12
12
 
13
13
  /** A link in the header nav. Nav links never turn visited purple. */
14
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
15
- const pageAttributes = config.current === "Page" ? [h.AriaCurrent("page")] : [];
16
- const sectionAttributes = config.current === "Section" ? [h.AriaCurrent("true")] : [];
17
- return h.a([h.Href(config.href), ...pageAttributes, ...sectionAttributes], [config.label]);
14
+ export const view = <Message>(
15
+ config: ViewConfig,
16
+ h: HtmlBuilder<Message>
17
+ ): Html => {
18
+ const pageAttributes =
19
+ config.current === "Page" ? [h.AriaCurrent("page")] : [];
20
+
21
+ const sectionAttributes =
22
+ config.current === "Section" ? [h.AriaCurrent("true")] : [];
23
+
24
+ return h.a(
25
+ [h.Href(config.href), ...pageAttributes, ...sectionAttributes],
26
+ [config.label]
27
+ );
18
28
  };
package/src/note/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { view } from "./note.ts";
2
+
2
3
  export type { ViewConfig } from "./note.ts";
package/src/note/note.ts CHANGED
@@ -10,10 +10,14 @@ export type ViewConfig = Readonly<{
10
10
 
11
11
  /** A ruled box for the thing a reader must not miss, its first word "Note." (or "Warning.")
12
12
  in bold; at most one per screen. */
13
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
13
+ export const view = <Message>(
14
+ config: ViewConfig,
15
+ h: HtmlBuilder<Message>
16
+ ): Html => {
14
17
  const { isWarn = false, body } = config;
18
+
15
19
  return h.aside(
16
20
  [h.Class(isWarn ? "note warn" : "note")],
17
- [h.p([], [h.strong([], [isWarn ? "Warning." : "Note."]), " ", ...body])],
21
+ [h.p([], [h.strong([], [isWarn ? "Warning." : "Note."]), " ", ...body])]
18
22
  );
19
23
  };
@@ -1,2 +1,3 @@
1
1
  export { view } from "./pager.ts";
2
+
2
3
  export type { Link, ViewConfig } from "./pager.ts";
@@ -13,8 +13,12 @@ export type ViewConfig = Readonly<{
13
13
 
14
14
  /** Older and newer links at the end of a post, then the way back to all posts. A missing
15
15
  neighbour becomes a muted line saying so. */
16
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
16
+ export const view = <Message>(
17
+ config: ViewConfig,
18
+ h: HtmlBuilder<Message>
19
+ ): Html => {
17
20
  const { newer, older, all } = config;
21
+
18
22
  return h.nav(
19
23
  [h.Class("pager"), h.AriaLabel("Older and newer posts")],
20
24
  [
@@ -25,6 +29,6 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
25
29
  ? h.p([h.Class("muted")], ["This is the oldest post."])
26
30
  : h.p([], ["Older: ", h.a([h.Href(older.href)], [older.label])]),
27
31
  h.p([], [h.a([h.Href(all.href)], [all.label])]),
28
- ],
32
+ ]
29
33
  );
30
34
  };
@@ -0,0 +1,3 @@
1
+ export { view } from "./post-list.ts";
2
+
3
+ export type { Entry, ViewConfig } from "./post-list.ts";
@@ -0,0 +1,30 @@
1
+ import type { Html, HtmlBuilder } from "foldkit/html";
2
+
3
+ export type Entry = Readonly<{
4
+ /** ISO date. */
5
+ date: string;
6
+ title: string;
7
+ href: string;
8
+ }>;
9
+
10
+ /** Configuration for rendering a post list with `view`. */
11
+ export type ViewConfig = Readonly<{ posts: readonly Entry[] }>;
12
+
13
+ /** A list of posts: date, then title, in two columns. */
14
+ export const view = <Message>(
15
+ config: ViewConfig,
16
+ h: HtmlBuilder<Message>
17
+ ): Html =>
18
+ h.ul(
19
+ [h.Class("posts")],
20
+ config.posts.map((post) =>
21
+ h.keyed("li")(
22
+ post.href,
23
+ [],
24
+ [
25
+ h.time([h.Datetime(post.date)], [post.date]),
26
+ h.a([h.Href(post.href)], [post.title]),
27
+ ]
28
+ )
29
+ )
30
+ );
@@ -0,0 +1,3 @@
1
+ export { view } from "./post-meta.ts";
2
+
3
+ export type { ViewConfig } from "./post-meta.ts";
@@ -10,7 +10,10 @@ export type ViewConfig = Readonly<{
10
10
  }>;
11
11
 
12
12
  /** Everything about a post, on one muted line under its title: the date, then the rest. */
13
- export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html =>
13
+ export const view = <Message>(
14
+ config: ViewConfig,
15
+ h: HtmlBuilder<Message>
16
+ ): Html =>
14
17
  h.p(
15
18
  [h.Class("meta")],
16
19
  [
@@ -19,5 +22,5 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
19
22
  onEmpty: () => [],
20
23
  onNonEmpty: (body) => [" · ", ...body],
21
24
  }),
22
- ],
25
+ ]
23
26
  );
@@ -1,3 +1,5 @@
1
1
  export { Scheme } from "./scheme.ts";
2
+
2
3
  export { schemeScript } from "./script.ts";
3
- export * as SchemeSwitch from "./schemeSwitch.ts";
4
+
5
+ export * as SchemeSwitch from "./scheme-switch.ts";
@@ -3,11 +3,20 @@ import type { Html } from "foldkit/html";
3
3
  import { defineView } from "foldkit/submodel";
4
4
 
5
5
  import type { Scheme } from "./scheme.ts";
6
- import { Message, type Model } from "./switch.ts";
6
+ import { Message } from "./switch.ts";
7
+ import type { Model } from "./switch.ts";
7
8
 
8
- export * from "./switch.ts";
9
+ export {
10
+ Message,
11
+ Model,
12
+ PinScheme,
13
+ ReadScheme,
14
+ id,
15
+ init,
16
+ update,
17
+ } from "./switch.ts";
9
18
 
10
- // VIEW
19
+ export type { InitConfig } from "./switch.ts";
11
20
 
12
21
  const CHOICES: ReadonlyArray<Readonly<{ scheme: Scheme; label: string }>> = [
13
22
  { scheme: "System", label: "system" },
@@ -31,13 +40,16 @@ export const view = defineView<Model, Message>((model, h): Html =>
31
40
  onClick: Message.SelectedScheme({ scheme: choice }),
32
41
  toView: ({ button }) =>
33
42
  h.button(
34
- [...button, h.AriaPressed(choice === model.scheme ? "true" : "false")],
35
- [label],
43
+ [
44
+ ...button,
45
+ h.AriaPressed(choice === model.scheme ? "true" : "false"),
46
+ ],
47
+ [label]
36
48
  ),
37
49
  },
38
- h,
50
+ h
39
51
  ),
40
52
  ]),
41
- ],
42
- ),
53
+ ]
54
+ )
43
55
  );
@@ -3,23 +3,22 @@ import { Match, Option, Schema } from "effect";
3
3
  /** The colour scheme: `Light` or `Dark` pinned on `<html data-scheme>`, or `System`, which
4
4
  follows the reader's system in CSS alone. */
5
5
  export const Scheme = Schema.Literals(["System", "Light", "Dark"]);
6
+
6
7
  export type Scheme = typeof Scheme.Type;
7
8
 
8
- /** The `data-scheme` value a scheme pins, or none for `System`. */
9
9
  export const toPinnedValue = (scheme: Scheme): Option.Option<string> =>
10
10
  Match.value(scheme).pipe(
11
11
  Match.withReturnType<Option.Option<string>>(),
12
12
  Match.when("System", () => Option.none()),
13
13
  Match.when("Light", () => Option.some("light")),
14
14
  Match.when("Dark", () => Option.some("dark")),
15
- Match.exhaustive,
15
+ Match.exhaustive
16
16
  );
17
17
 
18
- /** The scheme a `data-scheme` value pins. Anything else, or nothing, is `System`. */
19
18
  export const fromPinnedValue = (value: string | null | undefined): Scheme =>
20
19
  Match.value(value).pipe(
21
20
  Match.withReturnType<Scheme>(),
22
21
  Match.when("light", () => "Light"),
23
22
  Match.when("dark", () => "Dark"),
24
- Match.orElse(() => "System"),
23
+ Match.orElse(() => "System")
25
24
  );
@@ -7,20 +7,14 @@ import type * as Update from "foldkit/update";
7
7
  import { Scheme, fromPinnedValue, toPinnedValue } from "./scheme.ts";
8
8
  import { SCHEME_ATTRIBUTE, SCHEME_STORAGE_KEY } from "./script.ts";
9
9
 
10
- // The scheme switch without its view, the same file in every system that has it; the view
11
- // is the system's own, in schemeSwitch.ts, which also passes all of this on.
12
-
13
- // MODEL
14
-
15
10
  /** Schema for the scheme switch: its id, and the scheme in force as far as the page has
16
11
  seen. */
17
12
  export const Model = Schema.Struct({
18
13
  id: Schema.String,
19
14
  scheme: Scheme,
20
15
  });
21
- export type Model = typeof Model.Type;
22
16
 
23
- // MESSAGE
17
+ export type Model = typeof Model.Type;
24
18
 
25
19
  /** Union of all Messages the scheme switch can produce. */
26
20
  export const Message = defineMessageUnion({
@@ -28,9 +22,8 @@ export const Message = defineMessageUnion({
28
22
  CompletedPinScheme: {},
29
23
  CompletedReadScheme: { scheme: Scheme },
30
24
  });
31
- export type Message = typeof Message.Type;
32
25
 
33
- // COMMAND
26
+ export type Message = typeof Message.Type;
34
27
 
35
28
  /** Pins a scheme on `<html data-scheme>` and remembers it on this device in `localStorage`
36
29
  under the key `schemeScript` reads; `System` removes both. The attribute is written first,
@@ -40,7 +33,7 @@ export const PinScheme = Command.define("PinScheme", {
40
33
  args: { scheme: Scheme },
41
34
  messages: [Message.CompletedPinScheme],
42
35
  execute: ({ scheme }) =>
43
- Effect.try(() =>
36
+ Effect.try(() => {
44
37
  Option.match(toPinnedValue(scheme), {
45
38
  onNone: () => {
46
39
  document.documentElement.removeAttribute(SCHEME_ATTRIBUTE);
@@ -50,8 +43,8 @@ export const PinScheme = Command.define("PinScheme", {
50
43
  document.documentElement.setAttribute(SCHEME_ATTRIBUTE, value);
51
44
  localStorage.setItem(SCHEME_STORAGE_KEY, value);
52
45
  },
53
- }),
54
- ).pipe(Effect.ignore, Effect.as(Message.CompletedPinScheme())),
46
+ });
47
+ }).pipe(Effect.ignore, Effect.as(Message.CompletedPinScheme())),
55
48
  });
56
49
 
57
50
  /** Reads the scheme in force from `<html data-scheme>`: what `schemeScript` pinned before
@@ -60,13 +53,13 @@ export const ReadScheme = Command.define("ReadScheme", {
60
53
  messages: [Message.CompletedReadScheme],
61
54
  execute: Effect.sync(() =>
62
55
  Message.CompletedReadScheme({
63
- scheme: fromPinnedValue(document.documentElement.getAttribute(SCHEME_ATTRIBUTE)),
64
- }),
56
+ scheme: fromPinnedValue(
57
+ document.documentElement.getAttribute(SCHEME_ATTRIBUTE)
58
+ ),
59
+ })
65
60
  ),
66
61
  });
67
62
 
68
- // INIT
69
-
70
63
  /** Configuration for creating a switch with `init`: its id, unique in the document. */
71
64
  export type InitConfig = Readonly<{ id: string }>;
72
65
 
@@ -78,13 +71,9 @@ export const init = (config: InitConfig): Update.Return<Model, Message> => ({
78
71
  commands: [ReadScheme()],
79
72
  });
80
73
 
81
- // ACCESSORS
82
-
83
74
  /** The switch's id. */
84
75
  export const id = (model: Model): string => model.id;
85
76
 
86
- // UPDATE
87
-
88
77
  /** Processes a scheme switch Message and returns the next Model and Commands. */
89
78
  export const update = (model: Model, message: Message) =>
90
79
  Message.match<Update.Return<Model, Message>>(message, {
@@ -2,13 +2,10 @@ import { Effect, Option, Schema } from "effect";
2
2
  import { defineMessageUnion } from "foldkit/message";
3
3
  import * as Mount from "foldkit/mount";
4
4
 
5
- // MESSAGE
6
-
7
5
  /** What the Mount reports: it is watching. Nothing in a Model follows it. */
8
6
  export const Message = defineMessageUnion({ CompletedWatchOverflow: {} });
9
- export type Message = typeof Message.Type;
10
7
 
11
- // MOUNT
8
+ export type Message = typeof Message.Type;
12
9
 
13
10
  /** Whether a block is wider than its box and scrolls sideways, checked when it mounts and
14
11
  whenever its own box changes size, as when the window resizes. A font arriving that
@@ -23,34 +20,43 @@ export const WatchOverflow = Mount.define("WatchOverflow", {
23
20
  Effect.gen(function* () {
24
21
  yield* Effect.acquireRelease(
25
22
  Effect.sync(() => {
23
+ const clear = () => {
24
+ element.removeAttribute("tabindex");
25
+
26
+ if (Option.isSome(maybeName)) {
27
+ element.removeAttribute("role");
28
+ element.removeAttribute("aria-label");
29
+ }
30
+ };
31
+
26
32
  const check = () => {
27
33
  if (element.scrollWidth > element.clientWidth) {
28
34
  element.setAttribute("tabindex", "0");
35
+
29
36
  if (Option.isSome(maybeName)) {
30
37
  element.setAttribute("role", "region");
31
- element.setAttribute("aria-label", `Code: ${maybeName.value}, scrolls sideways`);
38
+ element.setAttribute(
39
+ "aria-label",
40
+ `Code: ${maybeName.value}, scrolls sideways`
41
+ );
32
42
  }
33
43
  } else {
34
44
  clear();
35
45
  }
36
46
  };
37
- const clear = () => {
38
- element.removeAttribute("tabindex");
39
- if (Option.isSome(maybeName)) {
40
- element.removeAttribute("role");
41
- element.removeAttribute("aria-label");
42
- }
43
- };
47
+
44
48
  const resized = new ResizeObserver(check);
45
49
  resized.observe(element);
46
50
  check();
51
+
47
52
  return () => {
48
53
  resized.disconnect();
49
54
  clear();
50
55
  };
51
56
  }),
52
- (stop) => Effect.sync(stop),
57
+ (stop) => Effect.sync(stop)
53
58
  );
59
+
54
60
  return Message.CompletedWatchOverflow();
55
61
  }),
56
62
  });