@nanisoft/prism-ui 0.5.0 → 0.6.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/LICENSE +21 -0
- package/README.md +81 -0
- package/THIRD-PARTY-NOTICES.md +48 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/blocks/app-shell-01/app-shell.d.ts +38 -0
- package/dist/blocks/app-shell-01/app-shell.d.ts.map +1 -0
- package/dist/blocks/app-shell-01/app-shell.js +27 -0
- package/dist/blocks/app-shell-01/index.d.ts +3 -0
- package/dist/blocks/app-shell-01/index.d.ts.map +1 -0
- package/dist/blocks/app-shell-01/index.js +1 -0
- package/dist/blocks/auth-form-01/auth-form.d.ts +52 -0
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -0
- package/dist/blocks/auth-form-01/auth-form.js +23 -0
- package/dist/blocks/auth-form-01/index.d.ts +3 -0
- package/dist/blocks/auth-form-01/index.d.ts.map +1 -0
- package/dist/blocks/auth-form-01/index.js +1 -0
- package/dist/blocks/cta-01/cta.d.ts +83 -0
- package/dist/blocks/cta-01/cta.d.ts.map +1 -0
- package/dist/blocks/cta-01/cta.js +29 -0
- package/dist/blocks/cta-01/index.d.ts +3 -0
- package/dist/blocks/cta-01/index.d.ts.map +1 -0
- package/dist/blocks/cta-01/index.js +1 -0
- package/dist/blocks/data-table-01/data-table.d.ts +102 -0
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -0
- package/dist/blocks/data-table-01/data-table.js +116 -0
- package/dist/blocks/data-table-01/index.d.ts +3 -0
- package/dist/blocks/data-table-01/index.d.ts.map +1 -0
- package/dist/blocks/data-table-01/index.js +1 -0
- package/dist/blocks/feature-grid-01/feature-grid.d.ts +78 -0
- package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -0
- package/dist/blocks/feature-grid-01/feature-grid.js +24 -0
- package/dist/blocks/feature-grid-01/index.d.ts +3 -0
- package/dist/blocks/feature-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/feature-grid-01/index.js +1 -0
- package/dist/blocks/hero-01/hero.d.ts +59 -0
- package/dist/blocks/hero-01/hero.d.ts.map +1 -0
- package/dist/blocks/hero-01/hero.js +24 -0
- package/dist/blocks/hero-01/index.d.ts +2 -0
- package/dist/blocks/hero-01/index.d.ts.map +1 -0
- package/dist/blocks/hero-01/index.js +1 -0
- package/dist/blocks/index.d.ts +28 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +26 -0
- package/dist/blocks/instrument-panel-01/index.d.ts +3 -0
- package/dist/blocks/instrument-panel-01/index.d.ts.map +1 -0
- package/dist/blocks/instrument-panel-01/index.js +1 -0
- package/dist/blocks/instrument-panel-01/instrument-panel.d.ts +114 -0
- package/dist/blocks/instrument-panel-01/instrument-panel.d.ts.map +1 -0
- package/dist/blocks/instrument-panel-01/instrument-panel.js +64 -0
- package/dist/blocks/logo-strip-01/index.d.ts +3 -0
- package/dist/blocks/logo-strip-01/index.d.ts.map +1 -0
- package/dist/blocks/logo-strip-01/index.js +1 -0
- package/dist/blocks/logo-strip-01/logo-strip.d.ts +84 -0
- package/dist/blocks/logo-strip-01/logo-strip.d.ts.map +1 -0
- package/dist/blocks/logo-strip-01/logo-strip.js +40 -0
- package/dist/blocks/note-grid-01/index.d.ts +3 -0
- package/dist/blocks/note-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/note-grid-01/index.js +1 -0
- package/dist/blocks/note-grid-01/note-grid.d.ts +79 -0
- package/dist/blocks/note-grid-01/note-grid.d.ts.map +1 -0
- package/dist/blocks/note-grid-01/note-grid.js +37 -0
- package/dist/blocks/page-header-01/index.d.ts +3 -0
- package/dist/blocks/page-header-01/index.d.ts.map +1 -0
- package/dist/blocks/page-header-01/index.js +1 -0
- package/dist/blocks/page-header-01/page-header.d.ts +48 -0
- package/dist/blocks/page-header-01/page-header.d.ts.map +1 -0
- package/dist/blocks/page-header-01/page-header.js +27 -0
- package/dist/blocks/pricing-01/index.d.ts +2 -0
- package/dist/blocks/pricing-01/index.d.ts.map +1 -0
- package/dist/blocks/pricing-01/index.js +1 -0
- package/dist/blocks/pricing-01/pricing.d.ts +35 -0
- package/dist/blocks/pricing-01/pricing.d.ts.map +1 -0
- package/dist/blocks/pricing-01/pricing.js +20 -0
- package/dist/blocks/process-rail-01/index.d.ts +3 -0
- package/dist/blocks/process-rail-01/index.d.ts.map +1 -0
- package/dist/blocks/process-rail-01/index.js +1 -0
- package/dist/blocks/process-rail-01/process-rail.d.ts +99 -0
- package/dist/blocks/process-rail-01/process-rail.d.ts.map +1 -0
- package/dist/blocks/process-rail-01/process-rail.js +58 -0
- package/dist/blocks/product-grid-01/index.d.ts +3 -0
- package/dist/blocks/product-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/product-grid-01/index.js +1 -0
- package/dist/blocks/product-grid-01/product-grid.d.ts +114 -0
- package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -0
- package/dist/blocks/product-grid-01/product-grid.js +51 -0
- package/dist/blocks/settings-panel-01/index.d.ts +3 -0
- package/dist/blocks/settings-panel-01/index.d.ts.map +1 -0
- package/dist/blocks/settings-panel-01/index.js +1 -0
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +71 -0
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -0
- package/dist/blocks/settings-panel-01/settings-panel.js +38 -0
- package/dist/blocks/site-footer/index.d.ts +3 -0
- package/dist/blocks/site-footer/index.d.ts.map +1 -0
- package/dist/blocks/site-footer/index.js +1 -0
- package/dist/blocks/site-footer/site-footer.d.ts +135 -0
- package/dist/blocks/site-footer/site-footer.d.ts.map +1 -0
- package/dist/blocks/site-footer/site-footer.js +41 -0
- package/dist/blocks/site-header/index.d.ts +3 -0
- package/dist/blocks/site-header/index.d.ts.map +1 -0
- package/dist/blocks/site-header/index.js +1 -0
- package/dist/blocks/site-header/site-header.d.ts +142 -0
- package/dist/blocks/site-header/site-header.d.ts.map +1 -0
- package/dist/blocks/site-header/site-header.js +46 -0
- package/dist/blocks/stack-grid-01/index.d.ts +3 -0
- package/dist/blocks/stack-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/stack-grid-01/index.js +1 -0
- package/dist/blocks/stack-grid-01/stack-grid.d.ts +118 -0
- package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -0
- package/dist/blocks/stack-grid-01/stack-grid.js +49 -0
- package/dist/blocks/stats-01/index.d.ts +2 -0
- package/dist/blocks/stats-01/index.d.ts.map +1 -0
- package/dist/blocks/stats-01/index.js +1 -0
- package/dist/blocks/stats-01/stats.d.ts +28 -0
- package/dist/blocks/stats-01/stats.d.ts.map +1 -0
- package/dist/blocks/stats-01/stats.js +27 -0
- package/dist/blocks/status-ledger-01/index.d.ts +3 -0
- package/dist/blocks/status-ledger-01/index.d.ts.map +1 -0
- package/dist/blocks/status-ledger-01/index.js +1 -0
- package/dist/blocks/status-ledger-01/status-ledger.d.ts +131 -0
- package/dist/blocks/status-ledger-01/status-ledger.d.ts.map +1 -0
- package/dist/blocks/status-ledger-01/status-ledger.js +80 -0
- package/dist/catalog.d.ts +28 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +672 -0
- package/dist/components/index.d.ts +64 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +43 -0
- package/dist/components/ui/accordion.d.ts +65 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/accordion.js +30 -0
- package/dist/components/ui/alert.d.ts +19 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.js +31 -0
- package/dist/components/ui/avatar.d.ts +47 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/avatar.js +30 -0
- package/dist/components/ui/badge.d.ts +14 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +26 -0
- package/dist/components/ui/breadcrumb.d.ts +35 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.js +48 -0
- package/dist/components/ui/button.d.ts +16 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +63 -0
- package/dist/components/ui/card.d.ts +41 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +53 -0
- package/dist/components/ui/checkbox.d.ts +43 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/checkbox.js +18 -0
- package/dist/components/ui/cta-link.d.ts +45 -0
- package/dist/components/ui/cta-link.d.ts.map +1 -0
- package/dist/components/ui/cta-link.js +81 -0
- package/dist/components/ui/diagram.d.ts +154 -0
- package/dist/components/ui/diagram.d.ts.map +1 -0
- package/dist/components/ui/diagram.js +150 -0
- package/dist/components/ui/dialog.d.ts +81 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dialog.js +60 -0
- package/dist/components/ui/dropdown-menu.d.ts +189 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.js +67 -0
- package/dist/components/ui/fact-list.d.ts +90 -0
- package/dist/components/ui/fact-list.d.ts.map +1 -0
- package/dist/components/ui/fact-list.js +38 -0
- package/dist/components/ui/field.d.ts +36 -0
- package/dist/components/ui/field.d.ts.map +1 -0
- package/dist/components/ui/field.js +46 -0
- package/dist/components/ui/input.d.ts +12 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +14 -0
- package/dist/components/ui/kbd.d.ts +10 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/kbd.js +12 -0
- package/dist/components/ui/pagination.d.ts +40 -0
- package/dist/components/ui/pagination.d.ts.map +1 -0
- package/dist/components/ui/pagination.js +49 -0
- package/dist/components/ui/popover.d.ts +56 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +25 -0
- package/dist/components/ui/product-mark.d.ts +115 -0
- package/dist/components/ui/product-mark.d.ts.map +1 -0
- package/dist/components/ui/product-mark.js +82 -0
- package/dist/components/ui/product-switcher.d.ts +108 -0
- package/dist/components/ui/product-switcher.d.ts.map +1 -0
- package/dist/components/ui/product-switcher.js +47 -0
- package/dist/components/ui/progress.d.ts +38 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/progress.js +17 -0
- package/dist/components/ui/prose.d.ts +88 -0
- package/dist/components/ui/prose.d.ts.map +1 -0
- package/dist/components/ui/prose.js +53 -0
- package/dist/components/ui/radio-group.d.ts +66 -0
- package/dist/components/ui/radio-group.d.ts.map +1 -0
- package/dist/components/ui/radio-group.js +30 -0
- package/dist/components/ui/section.d.ts +60 -0
- package/dist/components/ui/section.d.ts.map +1 -0
- package/dist/components/ui/section.js +21 -0
- package/dist/components/ui/select.d.ts +124 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +47 -0
- package/dist/components/ui/separator.d.ts +16 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/separator.js +15 -0
- package/dist/components/ui/skeleton.d.ts +12 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +14 -0
- package/dist/components/ui/slider.d.ts +43 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/slider.js +23 -0
- package/dist/components/ui/switch.d.ts +42 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +16 -0
- package/dist/components/ui/table.d.ts +27 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/table.js +43 -0
- package/dist/components/ui/tabs.d.ts +66 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tabs.js +29 -0
- package/dist/components/ui/textarea.d.ts +11 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/textarea.js +13 -0
- package/dist/components/ui/tooltip.d.ts +63 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +25 -0
- package/dist/components/ui/typography.d.ts +32 -0
- package/dist/components/ui/typography.d.ts.map +1 -0
- package/dist/components/ui/typography.js +51 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +5 -0
- package/dist/pages/auth-page/index.d.ts +36 -0
- package/dist/pages/auth-page/index.d.ts.map +1 -0
- package/dist/pages/auth-page/index.js +23 -0
- package/dist/pages/blog-post-page/index.d.ts +174 -0
- package/dist/pages/blog-post-page/index.d.ts.map +1 -0
- package/dist/pages/blog-post-page/index.js +49 -0
- package/dist/pages/dashboard-page/index.d.ts +36 -0
- package/dist/pages/dashboard-page/index.d.ts.map +1 -0
- package/dist/pages/dashboard-page/index.js +24 -0
- package/dist/pages/index.d.ts +21 -0
- package/dist/pages/index.d.ts.map +1 -0
- package/dist/pages/index.js +14 -0
- package/dist/pages/marketing-page/index.d.ts +43 -0
- package/dist/pages/marketing-page/index.d.ts.map +1 -0
- package/dist/pages/marketing-page/index.js +25 -0
- package/dist/pages/not-found-page/index.d.ts +112 -0
- package/dist/pages/not-found-page/index.d.ts.map +1 -0
- package/dist/pages/not-found-page/index.js +46 -0
- package/dist/pages/settings-page/index.d.ts +47 -0
- package/dist/pages/settings-page/index.d.ts.map +1 -0
- package/dist/pages/settings-page/index.js +24 -0
- package/dist/provider/index.d.ts +12 -0
- package/dist/provider/index.d.ts.map +1 -0
- package/dist/provider/index.js +9 -0
- package/dist/provider/provider.d.ts +42 -0
- package/dist/provider/provider.d.ts.map +1 -0
- package/dist/provider/provider.js +178 -0
- package/dist/provider/theme-script.d.ts +55 -0
- package/dist/provider/theme-script.d.ts.map +1 -0
- package/dist/provider/theme-script.js +79 -0
- package/dist/styles.css +2894 -0
- package/dist/theming/index.d.ts +173 -0
- package/dist/theming/index.d.ts.map +1 -0
- package/dist/theming/index.js +186 -0
- package/package.json +89 -18
- package/components.json +0 -21
- package/public/r/badge.json +0 -26
- package/public/r/button.json +0 -26
- package/public/r/card.json +0 -25
- package/public/r/cta-01.json +0 -44
- package/public/r/feature-grid-01.json +0 -44
- package/public/r/hero-01.json +0 -44
- package/public/r/pricing-01.json +0 -46
- package/public/r/registry.json +0 -294
- package/public/r/section.json +0 -25
- package/public/r/stats-01.json +0 -44
- package/registry.json +0 -294
- package/src/components/ds/blocks/cta-01/block.json +0 -8
- package/src/components/ds/blocks/cta-01/cta.tsx +0 -62
- package/src/components/ds/blocks/cta-01/index.tsx +0 -1
- package/src/components/ds/blocks/feature-grid-01/block.json +0 -8
- package/src/components/ds/blocks/feature-grid-01/feature-grid.tsx +0 -69
- package/src/components/ds/blocks/feature-grid-01/index.tsx +0 -1
- package/src/components/ds/blocks/hero-01/block.json +0 -8
- package/src/components/ds/blocks/hero-01/hero.tsx +0 -85
- package/src/components/ds/blocks/hero-01/index.tsx +0 -1
- package/src/components/ds/blocks/pricing-01/block.json +0 -8
- package/src/components/ds/blocks/pricing-01/index.tsx +0 -1
- package/src/components/ds/blocks/pricing-01/pricing.tsx +0 -122
- package/src/components/ds/blocks/section.meta.json +0 -6
- package/src/components/ds/blocks/section.tsx +0 -86
- package/src/components/ds/blocks/stats-01/block.json +0 -8
- package/src/components/ds/blocks/stats-01/index.tsx +0 -1
- package/src/components/ds/blocks/stats-01/stats.tsx +0 -92
- package/src/components/ui/badge.tsx +0 -37
- package/src/components/ui/button.tsx +0 -77
- package/src/components/ui/card.tsx +0 -65
- package/src/lib/utils.ts +0 -6
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* One way out of a page that was not found.
|
|
4
|
+
*
|
|
5
|
+
* A destination, and the words for it. Both are required, because a not-found
|
|
6
|
+
* page with no way out is a dead end with a message on it, and a link with no
|
|
7
|
+
* words is a shape a reader has to guess at.
|
|
8
|
+
*/
|
|
9
|
+
export type NotFoundLink = {
|
|
10
|
+
/** The words of the link. */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Where it goes. */
|
|
13
|
+
href: string;
|
|
14
|
+
/**
|
|
15
|
+
* Opens the destination in a new browsing context, which defaults the link
|
|
16
|
+
* relationship to `noopener noreferrer`. Almost never right here: a reader who
|
|
17
|
+
* followed a broken link and is sent to a new tab has to find their way back.
|
|
18
|
+
*/
|
|
19
|
+
newTab?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The props a NotFoundPage takes.
|
|
23
|
+
*
|
|
24
|
+
* Every string is a prop and the Page ships none. There is no "404", no "this
|
|
25
|
+
* page does not exist", no product name and no default set of links: a not-found
|
|
26
|
+
* page that hardcoded any of them would be a page that made a claim about a
|
|
27
|
+
* product it knows nothing about, and the one page on a site that every reader
|
|
28
|
+
* and no crawler will see is the worst place to hardcode a sentence.
|
|
29
|
+
*/
|
|
30
|
+
export type NotFoundPageProps = {
|
|
31
|
+
/**
|
|
32
|
+
* The status code, shown as the page's own `h1`. Required, and a string rather
|
|
33
|
+
* than a number because it is displayed, not computed: a Page never decides
|
|
34
|
+
* what code it is answering.
|
|
35
|
+
*/
|
|
36
|
+
code: string;
|
|
37
|
+
/**
|
|
38
|
+
* The headline under the code: the sentence that says what happened. It is the
|
|
39
|
+
* part a reader actually reads, and it is the caller's because every product
|
|
40
|
+
* says it differently - "No node here", "This page does not exist (yet)".
|
|
41
|
+
*/
|
|
42
|
+
title: string;
|
|
43
|
+
/**
|
|
44
|
+
* One or two sentences under the headline, for anything a reader should know
|
|
45
|
+
* before they follow one of the links: that the URL may be mistyped, that the
|
|
46
|
+
* page is planned, that a search would find it.
|
|
47
|
+
*/
|
|
48
|
+
description?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The ways out, in the order a reader should see them. Put the destination
|
|
51
|
+
* most likely to help first; a not-found page that offers a homepage link
|
|
52
|
+
* before the documentation sends a reader further from what they wanted.
|
|
53
|
+
*/
|
|
54
|
+
links: readonly NotFoundLink[];
|
|
55
|
+
/**
|
|
56
|
+
* The accessible name of the row of ways out. It is required, because it is a
|
|
57
|
+
* word a reader hears and a Page that ships no copy ships no reader-facing copy
|
|
58
|
+
* either. "Ways out" is the Page's own phrasing; a site that files its
|
|
59
|
+
* destinations differently names the same landmark differently.
|
|
60
|
+
*/
|
|
61
|
+
linksLabel: string;
|
|
62
|
+
/**
|
|
63
|
+
* A slot for whatever else the site wants to say here: a search box, a list of
|
|
64
|
+
* recent posts, a contact address.
|
|
65
|
+
*/
|
|
66
|
+
children?: ReactNode;
|
|
67
|
+
/**
|
|
68
|
+
* Layout only, exactly as on every Component. Changing a Prism-owned visual
|
|
69
|
+
* property from here is prohibited.
|
|
70
|
+
*/
|
|
71
|
+
className?: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* A complete not-found screen: the code, what happened, and the ways out.
|
|
75
|
+
*
|
|
76
|
+
* All four NaniSoft sites compose one, each in its own `not-found` route, and
|
|
77
|
+
* three of them are the same six lines with a different product name on it: a
|
|
78
|
+
* display title carrying the code, one muted sentence, and two or three links.
|
|
79
|
+
* The fourth wrote the same thing in its own markup. Four hand-written screens
|
|
80
|
+
* for one shape.
|
|
81
|
+
*
|
|
82
|
+
* It is a Page rather than a Block because it is a whole screen: it owns the
|
|
83
|
+
* document's `h1`, it is what a router renders when a route does not resolve, and
|
|
84
|
+
* it composes `Section`, `Heading`, `Text`, `Prose` and `CtaLink` into the one
|
|
85
|
+
* arrangement of them that answers "this is not here". A Block would be a band
|
|
86
|
+
* of that arrangement, and the band's job is not separable from the screen's:
|
|
87
|
+
* there is one correct page here and it is short.
|
|
88
|
+
*
|
|
89
|
+
* **The code is the `h1` and the sentence is not.** A not-found page whose
|
|
90
|
+
* `h1` is "Page not found" gives a search engine and a screen reader nothing
|
|
91
|
+
* distinct to index, and the code is the one string on the page that is the same
|
|
92
|
+
* for every broken URL. The code takes the display step and the sentence takes
|
|
93
|
+
* the muted one, which is the reverse of what a reader sees first and the right
|
|
94
|
+
* way round for anything that reads the outline.
|
|
95
|
+
*
|
|
96
|
+
* The links are `CtaLink`, so each one is a native anchor with a destination a
|
|
97
|
+
* browser can show, copy and open in a new tab. A not-found page is the one place
|
|
98
|
+
* on a site where a reader most wants to see where a link goes before following
|
|
99
|
+
* it, because they have just followed one that went nowhere.
|
|
100
|
+
*
|
|
101
|
+
* The page carries no `noindex` and no `robots` directive, because this is a
|
|
102
|
+
* component and not a route: what a not-found response is indexed as is the
|
|
103
|
+
* consumer's server's decision, and a Page that declared it would be a Page
|
|
104
|
+
* making a claim about a deployment it cannot see.
|
|
105
|
+
*
|
|
106
|
+
* It is a server Component. It fetches nothing, it holds no state and it imports
|
|
107
|
+
* no router, which is what lets a consumer render it from whichever not-found
|
|
108
|
+
* route their framework names.
|
|
109
|
+
*/
|
|
110
|
+
export declare function NotFoundPage({ code, title, description, links, linksLabel, children, className, }: NotFoundPageProps): import("react").JSX.Element;
|
|
111
|
+
export default NotFoundPage;
|
|
112
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/not-found-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BA+BnB;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CtaLink } from '../../components/ui/cta-link';
|
|
3
|
+
import { Heading, Text } from '../../components/ui/typography';
|
|
4
|
+
import { Prose } from '../../components/ui/prose';
|
|
5
|
+
import { Section } from '../../components/ui/section';
|
|
6
|
+
/**
|
|
7
|
+
* A complete not-found screen: the code, what happened, and the ways out.
|
|
8
|
+
*
|
|
9
|
+
* All four NaniSoft sites compose one, each in its own `not-found` route, and
|
|
10
|
+
* three of them are the same six lines with a different product name on it: a
|
|
11
|
+
* display title carrying the code, one muted sentence, and two or three links.
|
|
12
|
+
* The fourth wrote the same thing in its own markup. Four hand-written screens
|
|
13
|
+
* for one shape.
|
|
14
|
+
*
|
|
15
|
+
* It is a Page rather than a Block because it is a whole screen: it owns the
|
|
16
|
+
* document's `h1`, it is what a router renders when a route does not resolve, and
|
|
17
|
+
* it composes `Section`, `Heading`, `Text`, `Prose` and `CtaLink` into the one
|
|
18
|
+
* arrangement of them that answers "this is not here". A Block would be a band
|
|
19
|
+
* of that arrangement, and the band's job is not separable from the screen's:
|
|
20
|
+
* there is one correct page here and it is short.
|
|
21
|
+
*
|
|
22
|
+
* **The code is the `h1` and the sentence is not.** A not-found page whose
|
|
23
|
+
* `h1` is "Page not found" gives a search engine and a screen reader nothing
|
|
24
|
+
* distinct to index, and the code is the one string on the page that is the same
|
|
25
|
+
* for every broken URL. The code takes the display step and the sentence takes
|
|
26
|
+
* the muted one, which is the reverse of what a reader sees first and the right
|
|
27
|
+
* way round for anything that reads the outline.
|
|
28
|
+
*
|
|
29
|
+
* The links are `CtaLink`, so each one is a native anchor with a destination a
|
|
30
|
+
* browser can show, copy and open in a new tab. A not-found page is the one place
|
|
31
|
+
* on a site where a reader most wants to see where a link goes before following
|
|
32
|
+
* it, because they have just followed one that went nowhere.
|
|
33
|
+
*
|
|
34
|
+
* The page carries no `noindex` and no `robots` directive, because this is a
|
|
35
|
+
* component and not a route: what a not-found response is indexed as is the
|
|
36
|
+
* consumer's server's decision, and a Page that declared it would be a Page
|
|
37
|
+
* making a claim about a deployment it cannot see.
|
|
38
|
+
*
|
|
39
|
+
* It is a server Component. It fetches nothing, it holds no state and it imports
|
|
40
|
+
* no router, which is what lets a consumer render it from whichever not-found
|
|
41
|
+
* route their framework names.
|
|
42
|
+
*/
|
|
43
|
+
export function NotFoundPage({ code, title, description, links, linksLabel, children, className, }) {
|
|
44
|
+
return (_jsx(Section, { className: className, children: _jsxs("div", { className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: "h1", size: "4xl", className: "text-muted-foreground font-mono", children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h2", size: "2xl", children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
|
|
45
|
+
}
|
|
46
|
+
export default NotFoundPage;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
import { AppShell01 } from '../../blocks/app-shell-01';
|
|
3
|
+
import { PageHeader01 } from '../../blocks/page-header-01';
|
|
4
|
+
import { SettingsPanel01 } from '../../blocks/settings-panel-01';
|
|
5
|
+
export type SettingsPageTab = {
|
|
6
|
+
/** The value shared by the trigger and its panel. */
|
|
7
|
+
value: string;
|
|
8
|
+
/** The trigger label. */
|
|
9
|
+
label: string;
|
|
10
|
+
/**
|
|
11
|
+
* A saved settings form for this tab. Rendered through `SettingsPanel01`, so
|
|
12
|
+
* the tab inherits the grouped sections, the field kinds and the save footer.
|
|
13
|
+
*/
|
|
14
|
+
panel?: ComponentProps<typeof SettingsPanel01>;
|
|
15
|
+
/** Content for a tab that is not a saved form, such as a read-only summary. */
|
|
16
|
+
content?: ReactNode;
|
|
17
|
+
};
|
|
18
|
+
export type SettingsPageProps = {
|
|
19
|
+
/** The frame: rail, top bar and mobile navigation sheet. */
|
|
20
|
+
shell: Omit<ComponentProps<typeof AppShell01>, 'children'>;
|
|
21
|
+
/** The page title band. */
|
|
22
|
+
header: ComponentProps<typeof PageHeader01>;
|
|
23
|
+
/** The tabs, in order. At least one is required. */
|
|
24
|
+
tabs: SettingsPageTab[];
|
|
25
|
+
/** The tab shown first, for uncontrolled tabs. Defaults to the first tab. */
|
|
26
|
+
defaultValue?: string;
|
|
27
|
+
/** The controlled active tab. */
|
|
28
|
+
value?: string;
|
|
29
|
+
onValueChange?: (value: string) => void;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A complete settings screen.
|
|
33
|
+
*
|
|
34
|
+
* It composes `AppShell01`, `PageHeader01`, `Tabs` and `SettingsPanel01`: the
|
|
35
|
+
* shell provides the frame, the header names the screen, and each tab is a
|
|
36
|
+
* saved settings form or a read-only panel. The page owns the sequence and the
|
|
37
|
+
* tab wiring only. Every section, field, option and label is a prop, and the
|
|
38
|
+
* page fetches nothing.
|
|
39
|
+
*
|
|
40
|
+
* A tab that carries a `panel` renders `SettingsPanel01`, so a settings screen
|
|
41
|
+
* is a set of grouped forms rather than one long page. A tab that carries
|
|
42
|
+
* `content` instead renders the node as-is. Both are optional, so a caller can
|
|
43
|
+
* mix forms and read-only views in one screen.
|
|
44
|
+
*/
|
|
45
|
+
export declare function SettingsPage({ shell, header, tabs, defaultValue, value, onValueChange, }: SettingsPageProps): import("react").JSX.Element;
|
|
46
|
+
export default SettingsPage;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/settings-page/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAGhE,MAAM,MAAM,eAAe,GAAG;IAC5B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,eAAe,CAAC,CAAA;IAC9C,+EAA+E;IAC/E,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4DAA4D;IAC5D,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,UAAU,CAAC,EAAE,UAAU,CAAC,CAAA;IAC1D,2BAA2B;IAC3B,MAAM,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAA;IAC3C,oDAAoD;IACpD,IAAI,EAAE,eAAe,EAAE,CAAA;IACvB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,aAAa,GACd,EAAE,iBAAiB,+BA4BnB;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AppShell01 } from '../../blocks/app-shell-01';
|
|
4
|
+
import { PageHeader01 } from '../../blocks/page-header-01';
|
|
5
|
+
import { SettingsPanel01 } from '../../blocks/settings-panel-01';
|
|
6
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/ui/tabs';
|
|
7
|
+
/**
|
|
8
|
+
* A complete settings screen.
|
|
9
|
+
*
|
|
10
|
+
* It composes `AppShell01`, `PageHeader01`, `Tabs` and `SettingsPanel01`: the
|
|
11
|
+
* shell provides the frame, the header names the screen, and each tab is a
|
|
12
|
+
* saved settings form or a read-only panel. The page owns the sequence and the
|
|
13
|
+
* tab wiring only. Every section, field, option and label is a prop, and the
|
|
14
|
+
* page fetches nothing.
|
|
15
|
+
*
|
|
16
|
+
* A tab that carries a `panel` renders `SettingsPanel01`, so a settings screen
|
|
17
|
+
* is a set of grouped forms rather than one long page. A tab that carries
|
|
18
|
+
* `content` instead renders the node as-is. Both are optional, so a caller can
|
|
19
|
+
* mix forms and read-only views in one screen.
|
|
20
|
+
*/
|
|
21
|
+
export function SettingsPage({ shell, header, tabs, defaultValue, value, onValueChange, }) {
|
|
22
|
+
return (_jsx(AppShell01, { ...shell, children: _jsxs("div", { className: "mx-auto flex w-full max-w-page flex-col gap-8", children: [_jsx(PageHeader01, { ...header, headingLevel: header.headingLevel ?? 'h1' }), _jsxs(Tabs, { defaultValue: defaultValue ?? tabs[0]?.value, value: value, onValueChange: onValueChange, children: [_jsx(TabsList, { children: tabs.map((tab) => (_jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value))) }), tabs.map((tab) => (_jsx(TabsContent, { value: tab.value, children: tab.panel ? _jsx(SettingsPanel01, { ...tab.panel }) : tab.content }, tab.value)))] })] }) }));
|
|
23
|
+
}
|
|
24
|
+
export default SettingsPage;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Prism provider entry.
|
|
3
|
+
*
|
|
4
|
+
* `PrismProvider` and `usePrismTheme` are the optional programmatic path;
|
|
5
|
+
* `PrismThemeScript` is the optional pre-hydration script. The declarative
|
|
6
|
+
* `data-pack` and `.dark` attributes are the default and need none of this.
|
|
7
|
+
*/
|
|
8
|
+
export { PrismProvider, usePrismTheme } from './provider';
|
|
9
|
+
export type { PrismProviderProps, PrismTheme } from './provider';
|
|
10
|
+
export { PrismThemeScript } from './theme-script';
|
|
11
|
+
export type { PrismThemeScriptProps } from './theme-script';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Prism provider entry.
|
|
3
|
+
*
|
|
4
|
+
* `PrismProvider` and `usePrismTheme` are the optional programmatic path;
|
|
5
|
+
* `PrismThemeScript` is the optional pre-hydration script. The declarative
|
|
6
|
+
* `data-pack` and `.dark` attributes are the default and need none of this.
|
|
7
|
+
*/
|
|
8
|
+
export { PrismProvider, usePrismTheme } from './provider';
|
|
9
|
+
export { PrismThemeScript } from './theme-script';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The optional client runtime for programmatic theme switching.
|
|
3
|
+
*
|
|
4
|
+
* The provider is strictly additive: a consumer who puts `data-pack` and
|
|
5
|
+
* `.dark` on `<html>` never needs it, and a consumer who wants no client
|
|
6
|
+
* JavaScript omits it. It carries the pack and mode through context and writes
|
|
7
|
+
* the same two markup attributes the declarative form uses. There is no
|
|
8
|
+
* override, merge, token, className or style prop: the provider's only outputs
|
|
9
|
+
* are the document-element attributes and context (ticket 07).
|
|
10
|
+
*
|
|
11
|
+
* It resolves through `resolveTheme`, the one rule the boot script also
|
|
12
|
+
* implements, and it never writes `THEME_ORIGIN_ATTRIBUTE`: the script is that
|
|
13
|
+
* attribute's only writer, and two writers would turn a record of the origin
|
|
14
|
+
* into a race.
|
|
15
|
+
*/
|
|
16
|
+
import { type ReactNode } from 'react';
|
|
17
|
+
import { type Mode, type PackId } from '../theming';
|
|
18
|
+
export type PrismTheme = {
|
|
19
|
+
pack: PackId;
|
|
20
|
+
mode: Mode;
|
|
21
|
+
setPack: (pack: PackId) => void;
|
|
22
|
+
setMode: (mode: Mode) => void;
|
|
23
|
+
toggleMode: () => void;
|
|
24
|
+
};
|
|
25
|
+
export type PrismProviderProps = {
|
|
26
|
+
/** Pack used when nothing is stored and no pack attribute is present. */
|
|
27
|
+
defaultPack?: PackId;
|
|
28
|
+
/** Mode used in the same fallback. */
|
|
29
|
+
defaultMode?: Mode;
|
|
30
|
+
/** localStorage key for the persisted `{ pack, mode }` object. */
|
|
31
|
+
storageKey?: string;
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
};
|
|
34
|
+
export declare function PrismProvider({ defaultPack, defaultMode, storageKey, children, }: PrismProviderProps): import("react").JSX.Element;
|
|
35
|
+
/**
|
|
36
|
+
* Reads and mutates the active pack and mode.
|
|
37
|
+
*
|
|
38
|
+
* Throws outside a `PrismProvider`. The provider is optional, so a consumer
|
|
39
|
+
* that does not mount it simply does not call this hook.
|
|
40
|
+
*/
|
|
41
|
+
export declare function usePrismTheme(): PrismTheme;
|
|
42
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/provider/provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAEd,OAAO,EAML,KAAK,IAAI,EACT,KAAK,MAAM,EAEZ,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB,CAAA;AAID,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAyCD,wBAAgB,aAAa,CAAC,EAC5B,WAAuB,EACvB,WAAqB,EACrB,UAAgC,EAChC,QAAQ,GACT,EAAE,kBAAkB,+BAuHpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAM1C"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* The optional client runtime for programmatic theme switching.
|
|
5
|
+
*
|
|
6
|
+
* The provider is strictly additive: a consumer who puts `data-pack` and
|
|
7
|
+
* `.dark` on `<html>` never needs it, and a consumer who wants no client
|
|
8
|
+
* JavaScript omits it. It carries the pack and mode through context and writes
|
|
9
|
+
* the same two markup attributes the declarative form uses. There is no
|
|
10
|
+
* override, merge, token, className or style prop: the provider's only outputs
|
|
11
|
+
* are the document-element attributes and context (ticket 07).
|
|
12
|
+
*
|
|
13
|
+
* It resolves through `resolveTheme`, the one rule the boot script also
|
|
14
|
+
* implements, and it never writes `THEME_ORIGIN_ATTRIBUTE`: the script is that
|
|
15
|
+
* attribute's only writer, and two writers would turn a record of the origin
|
|
16
|
+
* into a race.
|
|
17
|
+
*/
|
|
18
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
19
|
+
import { DEFAULT_STORAGE_KEY, PACK_ATTRIBUTE, readLegacyMode, resolveTheme, themeAttributes, } from '../theming';
|
|
20
|
+
const PrismThemeContext = createContext(null);
|
|
21
|
+
function applyDocumentAttributes(pack, mode) {
|
|
22
|
+
const root = document.documentElement;
|
|
23
|
+
const { 'data-pack': packAttribute, className } = themeAttributes({ pack, mode });
|
|
24
|
+
if (packAttribute)
|
|
25
|
+
root.setAttribute(PACK_ATTRIBUTE, packAttribute);
|
|
26
|
+
else
|
|
27
|
+
root.removeAttribute(PACK_ATTRIBUTE);
|
|
28
|
+
if (className)
|
|
29
|
+
root.classList.add('dark');
|
|
30
|
+
else
|
|
31
|
+
root.classList.remove('dark');
|
|
32
|
+
}
|
|
33
|
+
/** One guarded read. A blocked or full store must not break the page. */
|
|
34
|
+
function read(key) {
|
|
35
|
+
try {
|
|
36
|
+
return localStorage.getItem(key);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** One guarded write. The attributes are already applied when this fails. */
|
|
43
|
+
function persist(key, pack, mode) {
|
|
44
|
+
try {
|
|
45
|
+
localStorage.setItem(key, JSON.stringify({ pack, mode }));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// A blocked or full localStorage must not break theme switching. The
|
|
49
|
+
// attributes are already applied, so the visible behaviour is unchanged.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function retire(key) {
|
|
53
|
+
try {
|
|
54
|
+
localStorage.removeItem(key);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Nothing to do: a key that cannot be removed is read again next load,
|
|
58
|
+
// recovers the same decision, and writes the same pair.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function PrismProvider({ defaultPack = 'default', defaultMode = 'light', storageKey = DEFAULT_STORAGE_KEY, children, }) {
|
|
62
|
+
/*
|
|
63
|
+
* The resolution, and whether a decision has been made. Two pieces of state
|
|
64
|
+
* for two different questions, which is why the old `resolved` flag was not
|
|
65
|
+
* enough on its own:
|
|
66
|
+
*
|
|
67
|
+
* - `theme` is null until the store has been read. It gates APPLY, because
|
|
68
|
+
* applying the default over a server-rendered pack before resolution is
|
|
69
|
+
* the flash the resolution order exists to prevent. It carries the
|
|
70
|
+
* origin, so the recovery below can read it without a second lookup.
|
|
71
|
+
* - `decided` is a ref, not state, because it must be readable by the write
|
|
72
|
+
* effect without causing a render, and settable synchronously by a caller
|
|
73
|
+
* whose state update has not been committed yet. It gates WRITE.
|
|
74
|
+
*
|
|
75
|
+
* A ref also cannot be re-rendered into an extra effect pass, which is what
|
|
76
|
+
* would have made the write fire once for the flag and once for the value.
|
|
77
|
+
*/
|
|
78
|
+
const [theme, setTheme] = useState(null);
|
|
79
|
+
const decided = useRef(false);
|
|
80
|
+
/*
|
|
81
|
+
* Resolution on mount, through the one shared rule: the stored value, then a
|
|
82
|
+
* key this line no longer writes, then what the server rendered, then the
|
|
83
|
+
* defaults. A server-rendered pack therefore survives hydration.
|
|
84
|
+
*/
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const recovered = readLegacyMode(read);
|
|
87
|
+
const next = resolveTheme({
|
|
88
|
+
stored: read(storageKey),
|
|
89
|
+
legacy: recovered?.mode ?? null,
|
|
90
|
+
documentPack: document.documentElement.getAttribute(PACK_ATTRIBUTE),
|
|
91
|
+
documentDark: document.documentElement.classList.contains('dark'),
|
|
92
|
+
defaultPack,
|
|
93
|
+
defaultMode,
|
|
94
|
+
});
|
|
95
|
+
if (next.origin === 'legacy' && recovered) {
|
|
96
|
+
/*
|
|
97
|
+
* The recovery write. A decision taken from a key this line no longer
|
|
98
|
+
* writes is put in the key it does write, and the old key is retired in
|
|
99
|
+
* the same breath, which is what makes the clause self-disabling: the
|
|
100
|
+
* next load finds nothing to recover, so a reader is migrated once and
|
|
101
|
+
* the population the clause can still reach only ever shrinks.
|
|
102
|
+
*/
|
|
103
|
+
persist(storageKey, next.pack, next.mode);
|
|
104
|
+
retire(recovered.key);
|
|
105
|
+
}
|
|
106
|
+
// A caller that decided in a layout effect before this ran keeps its value.
|
|
107
|
+
if (!decided.current)
|
|
108
|
+
setTheme(next);
|
|
109
|
+
}, [storageKey, defaultPack, defaultMode]);
|
|
110
|
+
/*
|
|
111
|
+
* Apply always, once there is a resolution; persist only on a decision.
|
|
112
|
+
*
|
|
113
|
+
* The first resolution from the document or the defaults writes nothing, so a
|
|
114
|
+
* visitor who never touched the toggle ends the load with an empty store and
|
|
115
|
+
* a site that changes its default reaches everyone who has not chosen.
|
|
116
|
+
*/
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (!theme)
|
|
119
|
+
return;
|
|
120
|
+
applyDocumentAttributes(theme.pack, theme.mode);
|
|
121
|
+
if (decided.current)
|
|
122
|
+
persist(storageKey, theme.pack, theme.mode);
|
|
123
|
+
}, [theme, storageKey]);
|
|
124
|
+
const setPack = useCallback((next) => {
|
|
125
|
+
decided.current = true;
|
|
126
|
+
setTheme((current) => ({
|
|
127
|
+
pack: next,
|
|
128
|
+
mode: current?.mode ?? defaultMode,
|
|
129
|
+
origin: 'stored',
|
|
130
|
+
}));
|
|
131
|
+
}, [defaultMode]);
|
|
132
|
+
const setMode = useCallback((next) => {
|
|
133
|
+
decided.current = true;
|
|
134
|
+
setTheme((current) => ({
|
|
135
|
+
pack: current?.pack ?? defaultPack,
|
|
136
|
+
mode: next,
|
|
137
|
+
origin: 'stored',
|
|
138
|
+
}));
|
|
139
|
+
}, [defaultPack]);
|
|
140
|
+
/*
|
|
141
|
+
* The pair is a closed union of two members, so the other one is the other
|
|
142
|
+
* member. Spelling it out costs nothing and does not hide the coupling behind
|
|
143
|
+
* an index into a list this file does not otherwise need.
|
|
144
|
+
*
|
|
145
|
+
* The three setters all fall back to the consumer's own default rather than
|
|
146
|
+
* to the neutral one, so a decision taken before the store has been read
|
|
147
|
+
* pairs the chosen axis with the site's default and not with a guess.
|
|
148
|
+
*/
|
|
149
|
+
const toggleMode = useCallback(() => {
|
|
150
|
+
decided.current = true;
|
|
151
|
+
setTheme((current) => ({
|
|
152
|
+
pack: current?.pack ?? defaultPack,
|
|
153
|
+
mode: current?.mode === 'dark' ? 'light' : 'dark',
|
|
154
|
+
origin: 'stored',
|
|
155
|
+
}));
|
|
156
|
+
}, [defaultPack]);
|
|
157
|
+
const value = useMemo(() => ({
|
|
158
|
+
pack: theme?.pack ?? defaultPack,
|
|
159
|
+
mode: theme?.mode ?? defaultMode,
|
|
160
|
+
setPack,
|
|
161
|
+
setMode,
|
|
162
|
+
toggleMode,
|
|
163
|
+
}), [theme, defaultPack, defaultMode, setPack, setMode, toggleMode]);
|
|
164
|
+
return _jsx(PrismThemeContext.Provider, { value: value, children: children });
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Reads and mutates the active pack and mode.
|
|
168
|
+
*
|
|
169
|
+
* Throws outside a `PrismProvider`. The provider is optional, so a consumer
|
|
170
|
+
* that does not mount it simply does not call this hook.
|
|
171
|
+
*/
|
|
172
|
+
export function usePrismTheme() {
|
|
173
|
+
const value = useContext(PrismThemeContext);
|
|
174
|
+
if (!value) {
|
|
175
|
+
throw new Error('usePrismTheme must be used within a PrismProvider');
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The optional pre-hydration theme script.
|
|
3
|
+
*
|
|
4
|
+
* A Server Component that renders one blocking script. Placed in `<head>`, it
|
|
5
|
+
* applies the stored pack and mode before React hydrates, so the first paint is
|
|
6
|
+
* already correct. It is safe to omit; a consumer who renders the attributes on
|
|
7
|
+
* `<html>` needs no client runtime at all.
|
|
8
|
+
*
|
|
9
|
+
* It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
|
|
10
|
+
* attribute on every path it reaches, so the origin of the active theme is
|
|
11
|
+
* always readable and never merely absent.
|
|
12
|
+
*
|
|
13
|
+
* The string below is a second implementation of `resolveTheme`, not a copy of
|
|
14
|
+
* it that has been kept in step. A function cannot be shared with a string that
|
|
15
|
+
* a minifier rewrites: a renamed identifier inside the script's own `try` throws
|
|
16
|
+
* there, the catch swallows it, and the theme silently stops applying on every
|
|
17
|
+
* page. So the string implements the rule and
|
|
18
|
+
* `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
|
|
19
|
+
* values, which is the only thing that can hold them together.
|
|
20
|
+
*
|
|
21
|
+
* Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
|
|
22
|
+
* string runs before paint on every page and adding it to a table of component
|
|
23
|
+
* bundle sizes would corrupt the unit that table exists to measure.
|
|
24
|
+
*/
|
|
25
|
+
import { type Mode, type PackId } from '../theming';
|
|
26
|
+
/**
|
|
27
|
+
* The first half of the storage-migration clause: read one key this line no
|
|
28
|
+
* longer writes.
|
|
29
|
+
*
|
|
30
|
+
* Exported because `check-boot-budget.mjs` prices the clause rather than
|
|
31
|
+
* guessing at it. The emitted string contains this fragment verbatim, so its
|
|
32
|
+
* byte count is the clause's cost and not an estimate of it.
|
|
33
|
+
*/
|
|
34
|
+
export declare const THEME_MIGRATION_READ: string;
|
|
35
|
+
/**
|
|
36
|
+
* The second half: the clause retires itself.
|
|
37
|
+
*
|
|
38
|
+
* The recovery is the only write this string performs, and it writes the
|
|
39
|
+
* recovered pair into the key this line does write and removes the key it came
|
|
40
|
+
* from, in one guarded step. That is the clause's expiry by rule rather than by
|
|
41
|
+
* number: it fires only while a retired key still holds a mode, it fires at
|
|
42
|
+
* most once per reader because its own write is what makes the second load find
|
|
43
|
+
* nothing, and it is retired for good by deleting the last entry of
|
|
44
|
+
* `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
|
|
45
|
+
* and the gate reports how many keys are still live so the number is visible
|
|
46
|
+
* rather than remembered.
|
|
47
|
+
*/
|
|
48
|
+
export declare const THEME_MIGRATION_RETIRE = "if(G===\"legacy\"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}";
|
|
49
|
+
export type PrismThemeScriptProps = {
|
|
50
|
+
storageKey?: string;
|
|
51
|
+
defaultPack?: PackId;
|
|
52
|
+
defaultMode?: Mode;
|
|
53
|
+
};
|
|
54
|
+
export declare function PrismThemeScript({ storageKey, defaultPack, defaultMode, }: PrismThemeScriptProps): import("react").JSX.Element;
|
|
55
|
+
//# sourceMappingURL=theme-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-script.d.ts","sourceRoot":"","sources":["../../src/provider/theme-script.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAOL,KAAK,IAAI,EACT,KAAK,MAAM,EACZ,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,QAEvB,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,+HACyF,CAAA;AAE5H,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAgC,EAChC,WAAuB,EACvB,WAAqB,GACtB,EAAE,qBAAqB,+BA+BvB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The optional pre-hydration theme script.
|
|
4
|
+
*
|
|
5
|
+
* A Server Component that renders one blocking script. Placed in `<head>`, it
|
|
6
|
+
* applies the stored pack and mode before React hydrates, so the first paint is
|
|
7
|
+
* already correct. It is safe to omit; a consumer who renders the attributes on
|
|
8
|
+
* `<html>` needs no client runtime at all.
|
|
9
|
+
*
|
|
10
|
+
* It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
|
|
11
|
+
* attribute on every path it reaches, so the origin of the active theme is
|
|
12
|
+
* always readable and never merely absent.
|
|
13
|
+
*
|
|
14
|
+
* The string below is a second implementation of `resolveTheme`, not a copy of
|
|
15
|
+
* it that has been kept in step. A function cannot be shared with a string that
|
|
16
|
+
* a minifier rewrites: a renamed identifier inside the script's own `try` throws
|
|
17
|
+
* there, the catch swallows it, and the theme silently stops applying on every
|
|
18
|
+
* page. So the string implements the rule and
|
|
19
|
+
* `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
|
|
20
|
+
* values, which is the only thing that can hold them together.
|
|
21
|
+
*
|
|
22
|
+
* Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
|
|
23
|
+
* string runs before paint on every page and adding it to a table of component
|
|
24
|
+
* bundle sizes would corrupt the unit that table exists to measure.
|
|
25
|
+
*/
|
|
26
|
+
import { DEFAULT_STORAGE_KEY, LEGACY_MODE_STORAGE_KEYS, MODES, PACKS, PACK_ATTRIBUTE, THEME_ORIGIN_ATTRIBUTE, } from '../theming';
|
|
27
|
+
/**
|
|
28
|
+
* The first half of the storage-migration clause: read one key this line no
|
|
29
|
+
* longer writes.
|
|
30
|
+
*
|
|
31
|
+
* Exported because `check-boot-budget.mjs` prices the clause rather than
|
|
32
|
+
* guessing at it. The emitted string contains this fragment verbatim, so its
|
|
33
|
+
* byte count is the clause's cost and not an estimate of it.
|
|
34
|
+
*/
|
|
35
|
+
export const THEME_MIGRATION_READ = LEGACY_MODE_STORAGE_KEYS.map((key) => `try{var g=localStorage.getItem(${JSON.stringify(key)});if(M.indexOf(g)>-1){L=g;LK=${JSON.stringify(key)};}}catch(e){}`).join('');
|
|
36
|
+
/**
|
|
37
|
+
* The second half: the clause retires itself.
|
|
38
|
+
*
|
|
39
|
+
* The recovery is the only write this string performs, and it writes the
|
|
40
|
+
* recovered pair into the key this line does write and removes the key it came
|
|
41
|
+
* from, in one guarded step. That is the clause's expiry by rule rather than by
|
|
42
|
+
* number: it fires only while a retired key still holds a mode, it fires at
|
|
43
|
+
* most once per reader because its own write is what makes the second load find
|
|
44
|
+
* nothing, and it is retired for good by deleting the last entry of
|
|
45
|
+
* `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
|
|
46
|
+
* and the gate reports how many keys are still live so the number is visible
|
|
47
|
+
* rather than remembered.
|
|
48
|
+
*/
|
|
49
|
+
export const THEME_MIGRATION_RETIRE = 'if(G==="legacy"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}';
|
|
50
|
+
export function PrismThemeScript({ storageKey = DEFAULT_STORAGE_KEY, defaultPack = 'default', defaultMode = 'light', }) {
|
|
51
|
+
const script = '(function(){try{' +
|
|
52
|
+
'var r=document.documentElement,' +
|
|
53
|
+
`P=${JSON.stringify(PACKS)},` +
|
|
54
|
+
`M=${JSON.stringify(MODES)},` +
|
|
55
|
+
`K=${JSON.stringify(storageKey)},` +
|
|
56
|
+
`A=${JSON.stringify(PACK_ATTRIBUTE)},` +
|
|
57
|
+
`O=${JSON.stringify(THEME_ORIGIN_ATTRIBUTE)},` +
|
|
58
|
+
`DP=${JSON.stringify(defaultPack)},` +
|
|
59
|
+
`DM=${JSON.stringify(defaultMode)},` +
|
|
60
|
+
'L=null,LK=null,S=null,T=null;' +
|
|
61
|
+
'try{var t=localStorage.getItem(K);if(t){T=t;S=JSON.parse(t);}}catch(e){}' +
|
|
62
|
+
THEME_MIGRATION_READ +
|
|
63
|
+
'var a=r.getAttribute(A),' +
|
|
64
|
+
'DPK=P.indexOf(a)>-1?a:null,' +
|
|
65
|
+
'DK=r.classList.contains("dark")?"dark":null,' +
|
|
66
|
+
'U=S&&typeof S=="object"&&P.indexOf(S.pack)>-1&&M.indexOf(S.mode)>-1,' +
|
|
67
|
+
'pack,m,G;' +
|
|
68
|
+
'if(U){pack=S.pack;m=S.mode;G="stored";}' +
|
|
69
|
+
'else if(T){pack=DPK||DP;m=DK||DM;G="unparsed";}' +
|
|
70
|
+
'else if(L){pack=DP;m=L;G="legacy";}' +
|
|
71
|
+
'else if(DPK||DK){pack=DPK||DP;m=DK||DM;G="document";}' +
|
|
72
|
+
'else{pack=DP;m=DM;G="default";}' +
|
|
73
|
+
THEME_MIGRATION_RETIRE +
|
|
74
|
+
'if(pack!=="default"){r.setAttribute(A,pack);}else{r.removeAttribute(A);}' +
|
|
75
|
+
'r.classList.toggle("dark",m==="dark");' +
|
|
76
|
+
'r.setAttribute(O,G);' +
|
|
77
|
+
'}catch(e){}})();';
|
|
78
|
+
return _jsx("script", { dangerouslySetInnerHTML: { __html: script } });
|
|
79
|
+
}
|