@tenphi/renderer 0.20.3 → 0.20.5

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.
@@ -1,5 +1,6 @@
1
1
  import { tasty } from "@tenphi/tasty";
2
2
  import { customizeComponent } from "./customize-component.js";
3
+ import { selectPopoverStyles } from "./select-popover-styles.js";
3
4
  import { configureCookbookStates } from "./tasty-states.js";
4
5
 
5
6
  configureCookbookStates();
@@ -117,56 +118,37 @@ export const PackageVersionRoot = customizeComponent(
117
118
  }),
118
119
  );
119
120
 
121
+ const versionSelectStyles = selectPopoverStyles({
122
+ option: "Link",
123
+ hoverOption: "HoverLink",
124
+ currentOption: "CurrentLink",
125
+ });
126
+
120
127
  export const VersionSwitcherRoot = customizeComponent(
121
128
  "VersionSwitcher",
122
129
  tasty({
123
- as: "details",
130
+ as: "div",
124
131
  styles: {
132
+ ...versionSelectStyles,
125
133
  display: "inline-flex",
126
- position: "relative",
127
134
  flexShrink: "0",
128
- color: "#text-soft",
129
135
  Trigger: {
130
- $: "> summary",
131
- display: "flex",
132
- alignItems: "center",
136
+ ...versionSelectStyles.Trigger,
133
137
  blockSize: "min $control-height",
134
138
  inlinePadding: "$gap",
135
139
  fill: "#surface-2",
136
140
  border: true,
137
- radius: "$header-control-radius",
138
141
  preset: "small / strong",
139
- cursor: "pointer",
140
- whiteSpace: "nowrap",
142
+ anchorName: "--version-trigger",
141
143
  },
142
144
  Panel: {
143
- $: "> nav",
144
- position: "absolute",
145
- inset: { "": "100% auto auto 0", "@mobile": "100% 0 auto auto" },
146
- zIndex: "10",
147
- inlineSize: "min 8rem",
148
- padding: "($gap * 0.5)",
149
- fill: "#surface",
150
- border: true,
151
- radius: "$radius",
152
- shadow: "0 0.75rem 2rem #shadow",
153
- },
154
- OpenPanel: {
155
- $: "&[open] > nav",
156
- display: "grid",
157
- },
158
- Link: {
159
- $: "> nav > a",
160
- display: "block",
161
- padding: "($gap * 0.5) $gap",
162
- color: "#text-soft",
163
- preset: "small",
164
- textDecoration: "none",
165
- },
166
- CurrentLink: {
167
- $: '> nav > a[aria-current="page"]',
168
- color: "#accent-text",
169
- preset: "small / strong",
145
+ ...versionSelectStyles.Panel,
146
+ positionAnchor: "--version-trigger",
147
+ blockInset: "(anchor(bottom) + $gap) start, auto end",
148
+ inlineInset: {
149
+ "": "anchor(left) start, auto end",
150
+ "@mobile": "auto start, anchor(right) end",
151
+ },
170
152
  },
171
153
  },
172
154
  }),
@@ -182,7 +164,7 @@ export const TopNavigationRoot = customizeComponent(
182
164
  alignItems: "stretch",
183
165
  gap: "clamp(1.25rem, 2.5vw, 2.5rem)",
184
166
  inlineSize: "100%",
185
- blockSize: "min 2.5rem",
167
+ blockSize: "min 2.75rem",
186
168
  blockBorder: "1bw solid #border start",
187
169
  overflowX: "auto",
188
170
  scrollbar: "none",
@@ -196,7 +178,7 @@ export const TopNavigationRoot = customizeComponent(
196
178
  flexGrow: "0",
197
179
  flexShrink: "0",
198
180
  flexBasis: "auto",
199
- blockPadding: "($gap * 0.75) start, $gap end",
181
+ blockPadding: "($gap * 1.25) start, $gap end",
200
182
  color: "#text-soft",
201
183
  preset: "navigation",
202
184
  textDecoration: "none",
@@ -320,6 +302,15 @@ export const HeaderRoot = customizeComponent(
320
302
  inlineSize: "$docs-menu-button-size",
321
303
  blockSize: "$docs-menu-button-size",
322
304
  },
305
+ MobileLanguage: {
306
+ $: ".td-header__mobile-language",
307
+ display: "grid",
308
+ hide: { "": true, "@mobile": false },
309
+ flexShrink: "0",
310
+ order: "1",
311
+ inlineSize: "$docs-menu-button-size",
312
+ blockSize: "$docs-menu-button-size",
313
+ },
323
314
  },
324
315
  }),
325
316
  );
@@ -394,9 +385,9 @@ export const ThemeSelectRoot = customizeComponent(
394
385
  },
395
386
  },
396
387
  OpenPanel: {
397
- $: "[popover]:popover-open",
398
- opacity: { "": "1", "@starting": "0" },
399
- scale: { "": "1", "@starting": "1 0.96" },
388
+ $: "[popover]:popover-open[data-open]",
389
+ opacity: "1",
390
+ scale: "1",
400
391
  },
401
392
  Section: {
402
393
  $: "fieldset",
@@ -674,6 +665,7 @@ export const HeaderLinksRoot = customizeComponent(
674
665
  display: "flex",
675
666
  flexShrink: "0",
676
667
  order: { "": "0", "@mobile": "2" },
668
+ "$popover-transition": "120ms",
677
669
  Desktop: {
678
670
  $: ".td-header-links__desktop",
679
671
  display: "flex",
@@ -742,6 +734,19 @@ export const HeaderLinksRoot = customizeComponent(
742
734
  border: true,
743
735
  radius: "$card-radius",
744
736
  shadow: "0 0.75rem 2rem #shadow",
737
+ opacity: "0",
738
+ scale: "1 0.96",
739
+ transformOrigin: "top",
740
+ transition: {
741
+ "": "none",
742
+ "!@reduced-motion":
743
+ "opacity $popover-transition ease-out, scale $popover-transition ease-out, display $popover-transition allow-discrete, overlay $popover-transition allow-discrete",
744
+ },
745
+ },
746
+ OpenPanel: {
747
+ $: ".td-header-links__panel:popover-open[data-open]",
748
+ opacity: "1",
749
+ scale: "1",
745
750
  },
746
751
  PanelNavigation: {
747
752
  $: ".td-header-links__panel nav",
@@ -14,7 +14,7 @@ const ui = messages(
14
14
  ---
15
15
 
16
16
  <PreviewRoot
17
- className="td-preview"
17
+ className="td-preview not-content"
18
18
  aria-label={title}
19
19
  data-tasty-anatomy="Preview"
20
20
  >
@@ -0,0 +1,64 @@
1
+ ---
2
+ import Icon from "./Icon.astro";
3
+
4
+ interface Option {
5
+ href: string;
6
+ label: string;
7
+ lang?: string;
8
+ hint?: string;
9
+ current?: boolean;
10
+ }
11
+
12
+ interface Props {
13
+ triggerLabel: string;
14
+ selectedLabel: string;
15
+ title: string;
16
+ options: Option[];
17
+ icon?: string;
18
+ }
19
+
20
+ const { triggerLabel, selectedLabel, title, options, icon } = Astro.props;
21
+ const id = `select-${crypto.randomUUID()}`;
22
+ ---
23
+
24
+ <button
25
+ type="button"
26
+ popovertarget={id}
27
+ aria-label={triggerLabel}
28
+ title={title}
29
+ >
30
+ {icon && <Icon name={icon} class="label-icon" />}
31
+ <span class="trigger-label">{selectedLabel}</span>
32
+ <Icon name="down-caret" class="caret" />
33
+ </button>
34
+ <div
35
+ id={id}
36
+ popover="auto"
37
+ role="group"
38
+ aria-label={title}
39
+ data-select-popover-panel
40
+ >
41
+ <div class="panel-title">{title}</div>
42
+ <nav aria-label={title}>
43
+ {
44
+ options.map((option) => (
45
+ <a
46
+ href={option.href}
47
+ aria-current={option.current ? "page" : undefined}
48
+ >
49
+ <span lang={option.lang}>{option.label}</span>
50
+ {option.hint && <span class="fallback-label">({option.hint})</span>}
51
+ <Icon name="approve-check" class="checkmark" />
52
+ </a>
53
+ ))
54
+ }
55
+ </nav>
56
+ </div>
57
+
58
+ <script>
59
+ import { initializePopoverMotion } from "../client/popover-motion.js";
60
+
61
+ document
62
+ .querySelectorAll<HTMLElement>("[data-select-popover-panel]")
63
+ .forEach((panel) => initializePopoverMotion(panel));
64
+ </script>
@@ -84,13 +84,13 @@ export const PreviewRoot = customizeComponent(
84
84
  fill: "#surface-2",
85
85
  },
86
86
  Stage: {
87
- padding: "3x",
87
+ padding: "2x",
88
88
  fill: "#surface",
89
89
  },
90
90
  Frame: {
91
91
  display: "block",
92
92
  width: "100%",
93
- height: "min 18rem",
93
+ height: "min 10rem",
94
94
  border: "0",
95
95
  fill: "#surface",
96
96
  },
@@ -102,9 +102,23 @@ export const PreviewRoot = customizeComponent(
102
102
  preset: "small",
103
103
  fill: "#surface-2",
104
104
  cursor: "pointer",
105
+ transition: "fill $transition",
106
+ },
107
+ HoverSummary: {
108
+ $: "> details > summary:hover",
109
+ fill: "#surface-2-hover",
110
+ },
111
+ ActiveSummary: {
112
+ $: "> details > summary:active",
113
+ fill: "#surface-2-pressed",
105
114
  },
106
115
  Pre: {
107
116
  margin: "0",
117
+ padding: "2x",
118
+ overflowX: "auto",
119
+ color: "#syntax-text",
120
+ fill: "#syntax-bg",
121
+ preset: "code",
108
122
  radius: "0",
109
123
  },
110
124
  },
@@ -4,6 +4,7 @@ import { content } from "virtual:cookbook/config";
4
4
  import type { DocsRoute, SiteVersion } from "@tenphi/docs";
5
5
  import { versionLinks } from "../versioning.js";
6
6
  import { VersionSwitcherRoot } from "./LayoutComponents.js";
7
+ import SelectPopover from "./SelectPopover.astro";
7
8
 
8
9
  interface Props {
9
10
  versions: SiteVersion[];
@@ -23,22 +24,14 @@ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
23
24
  ---
24
25
 
25
26
  <VersionSwitcherRoot data-tasty-anatomy="VersionSwitcher">
26
- <summary
27
- aria-label={`${ui.documentationVersion}: ${current?.label ?? ui.versions}`}
28
- >{current?.label ?? ui.versions}</summary
29
- >
30
- <nav aria-label={ui.documentationVersions}>
31
- {
32
- links.map((version) => (
33
- <a
34
- href={version.href}
35
- aria-current={
36
- version.routeBase === current?.routeBase ? "page" : undefined
37
- }
38
- >
39
- {version.label}
40
- </a>
41
- ))
42
- }
43
- </nav>
27
+ <SelectPopover
28
+ triggerLabel={`${ui.documentationVersion}: ${current?.label ?? ui.versions}`}
29
+ selectedLabel={current?.label ?? ui.versions}
30
+ title={ui.documentationVersions}
31
+ options={links.map((version) => ({
32
+ href: version.href,
33
+ label: version.label,
34
+ current: version.routeBase === current?.routeBase,
35
+ }))}
36
+ />
44
37
  </VersionSwitcherRoot>
@@ -0,0 +1,107 @@
1
+ /** Shared server-side styles for the language and version selectors. */
2
+ export function selectPopoverStyles({
3
+ option = "Option",
4
+ hoverOption = "HoverOption",
5
+ currentOption = "CurrentOption",
6
+ } = {}) {
7
+ return {
8
+ "$popover-transition": "120ms",
9
+ Trigger: {
10
+ $: "> button",
11
+ display: "flex",
12
+ alignItems: "center",
13
+ justifyContent: "center",
14
+ gap: "($gap * 0.5)",
15
+ color: "#text-soft",
16
+ radius: "$header-control-radius",
17
+ preset: "small",
18
+ cursor: "pointer",
19
+ },
20
+ HoverTrigger: {
21
+ $: "> button:hover, &:has([popover]:popover-open) > button",
22
+ color: "#text",
23
+ fill: "#surface-2-hover",
24
+ },
25
+ TriggerLabel: {
26
+ $: ".trigger-label",
27
+ overflow: "hidden",
28
+ textOverflow: "ellipsis",
29
+ whiteSpace: "nowrap",
30
+ },
31
+ Caret: {
32
+ $: "> button > .caret",
33
+ flexShrink: "0",
34
+ inlineSize: "0.875rem",
35
+ blockSize: "0.875rem",
36
+ },
37
+ Panel: {
38
+ $: "> [popover]",
39
+ position: "fixed",
40
+ inset: "auto",
41
+ inlineSize: "min(15rem, calc(100vw - 2 * $docs-nav-pad-x))",
42
+ blockSize: "max (100dvh - $docs-nav-height - $gap)",
43
+ overflowY: "auto",
44
+ margin: "0",
45
+ padding: "$gap",
46
+ color: "#text",
47
+ fill: "#surface-2",
48
+ border: true,
49
+ radius: "$card-radius",
50
+ shadow: "0 0.75rem 2rem #shadow",
51
+ opacity: "0",
52
+ scale: "1 0.96",
53
+ transformOrigin: "top",
54
+ transition: {
55
+ "": "none",
56
+ "!@reduced-motion":
57
+ "opacity $popover-transition ease-out, scale $popover-transition ease-out, display $popover-transition allow-discrete, overlay $popover-transition allow-discrete",
58
+ },
59
+ },
60
+ OpenPanel: {
61
+ $: "> [popover]:popover-open[data-open]",
62
+ opacity: "1",
63
+ scale: "1",
64
+ },
65
+ PanelTitle: {
66
+ $: ".panel-title",
67
+ padding: "($gap * 0.75) $gap",
68
+ color: "#text-muted",
69
+ preset: "small / strong",
70
+ },
71
+ Options: { $: "nav", display: "grid", gap: "($gap * 0.25)" },
72
+ [option]: {
73
+ $: "nav a",
74
+ display: "flex",
75
+ alignItems: "center",
76
+ gap: "$gap",
77
+ minBlockSize: "2.25rem",
78
+ padding: "($gap * 0.75) $gap",
79
+ color: "#text-soft",
80
+ preset: "small",
81
+ textDecoration: "none",
82
+ radius: "$radius",
83
+ },
84
+ [hoverOption]: {
85
+ $: "nav a:hover",
86
+ color: "#text",
87
+ fill: "#surface-2-hover",
88
+ },
89
+ [currentOption]: {
90
+ $: 'nav a[aria-current="page"]',
91
+ color: "#accent-text",
92
+ fill: "#accent-surface-2-subtle",
93
+ },
94
+ Checkmark: {
95
+ $: "nav a > .checkmark",
96
+ flexShrink: "0",
97
+ inlineMargin: "auto start",
98
+ inlineSize: "1rem",
99
+ blockSize: "1rem",
100
+ visibility: "hidden",
101
+ },
102
+ SelectedCheckmark: {
103
+ $: 'nav a[aria-current="page"] > .checkmark',
104
+ visibility: "visible",
105
+ },
106
+ };
107
+ }
package/dist/index.js CHANGED
@@ -175,6 +175,17 @@ function assertTastyOutput(html, path) {
175
175
  visit(tree);
176
176
  }
177
177
  //#endregion
178
+ //#region src/pagefind-adapter.ts
179
+ /** Pagefind's text clear button measures itself and writes inline padding.
180
+ * Cookbook renders an icon and owns the reserved space in SearchResults.Input.
181
+ * Fail on an upstream change so a dependency upgrade cannot silently restore it.
182
+ */
183
+ function adaptPagefindUI(source) {
184
+ const assignment = /input_el\.style\.paddingRight = `\$\{width \+ 2\}px`/g;
185
+ if ([...source.matchAll(assignment)].length !== 1) throw new Error("Pagefind's UI sizing changed. Update Cookbook's Tasty adapter before upgrading @pagefind/default-ui.");
186
+ return source.replace(assignment, "undefined");
187
+ }
188
+ //#endregion
178
189
  //#region src/markdown/rehype-mermaid.ts
179
190
  const sourceStyleDirective = /^\s*(?:classDef|style|linkStyle)\s+.*$/gim;
180
191
  const accessibilityDirective = /^\s*acc(?:Title|Descr):\s*.*$/gim;
@@ -1263,12 +1274,21 @@ function configuredCookbook(options) {
1263
1274
  shikiConfig: cookbookShikiConfig(context.config.markdown.shikiConfig)
1264
1275
  },
1265
1276
  vite: {
1277
+ optimizeDeps: { exclude: ["@pagefind/default-ui"] },
1266
1278
  ssr: { external: [
1267
1279
  "@tenphi/docs",
1268
1280
  "react",
1269
1281
  "react-dom"
1270
1282
  ] },
1271
1283
  plugins: [
1284
+ {
1285
+ name: "cookbook-pagefind-ui",
1286
+ enforce: "pre",
1287
+ transform(source, id) {
1288
+ const modulePath = id.replaceAll("\\", "/").replace(/\?.*$/, "");
1289
+ if (modulePath.includes("/@pagefind/default-ui/npm_dist/") && /\/ui-core\.(?:mjs|cjs)$/.test(modulePath)) return adaptPagefindUI(source);
1290
+ }
1291
+ },
1272
1292
  {
1273
1293
  name: "cookbook-server-only-styling",
1274
1294
  enforce: "pre",