handoff-app 1.2.2-6 → 2.0.0-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/Changelog.md +37 -15
- package/README.md +372 -118
- package/dist/app-builder/build.d.ts +41 -2
- package/dist/app-builder/build.js +455 -18
- package/dist/app-builder/client-config.d.ts +15 -2
- package/dist/app-builder/client-config.js +43 -4
- package/dist/app-builder/config-diff/snapshot.d.ts +1 -6
- package/dist/app-builder/config-diff/snapshot.js +2 -9
- package/dist/app-builder/index.d.ts +1 -1
- package/dist/app-builder/index.js +3 -1
- package/dist/app-builder/nav-shell.d.ts +23 -0
- package/dist/app-builder/nav-shell.js +80 -0
- package/dist/app-builder/static-export.d.ts +17 -0
- package/dist/app-builder/static-export.js +181 -0
- package/dist/app-builder/vercel-output.d.ts +39 -0
- package/dist/app-builder/vercel-output.js +259 -0
- package/dist/app-builder/watchers/utils.d.ts +2 -0
- package/dist/app-builder/watchers.d.ts +14 -4
- package/dist/app-builder/watchers.js +123 -28
- package/dist/app-builder/websocket.js +1 -1
- package/dist/artifacts/index.d.ts +2 -0
- package/dist/artifacts/index.js +13 -0
- package/dist/artifacts/types.d.ts +136 -0
- package/dist/artifacts/types.js +24 -0
- package/dist/artifacts/url.d.ts +49 -0
- package/dist/artifacts/url.js +86 -0
- package/dist/cache/build-cache.js +48 -4
- package/dist/commands/build/app.js +1 -2
- package/dist/commands/build/index.d.ts +17 -0
- package/dist/commands/build/index.js +66 -0
- package/dist/commands/checkout/index.d.ts +24 -0
- package/dist/commands/checkout/index.js +71 -0
- package/dist/commands/db/migrate.d.ts +11 -0
- package/dist/commands/db/migrate.js +33 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/publish/index.d.ts +21 -0
- package/dist/commands/publish/index.js +68 -0
- package/dist/config/defaults.js +15 -1
- package/dist/config/loader.js +6 -1
- package/dist/config/normalizers/declaration.js +4 -1
- package/dist/config/normalizers/page.d.ts +18 -0
- package/dist/config/normalizers/page.js +34 -0
- package/dist/config/normalizers/pattern.js +7 -1
- package/dist/config/runtime.d.ts +4 -0
- package/dist/config/runtime.js +53 -5
- package/dist/index.d.ts +65 -1
- package/dist/index.js +123 -3
- package/dist/nav/index.d.ts +83 -0
- package/dist/nav/index.js +9 -0
- package/dist/nav/registry.d.ts +15 -0
- package/dist/nav/registry.js +42 -0
- package/dist/nav/resolver.d.ts +8 -0
- package/dist/nav/resolver.js +192 -0
- package/dist/nav/workspace.d.ts +18 -0
- package/dist/nav/workspace.js +103 -0
- package/dist/pipeline/styles.js +7 -35
- package/dist/pipeline/token-transformers.d.ts +45 -0
- package/dist/pipeline/token-transformers.js +71 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.d.ts +17 -0
- package/dist/registry/asset-storage/adapters/vercel-blob.js +87 -0
- package/dist/registry/asset-storage/define.d.ts +10 -0
- package/dist/registry/asset-storage/define.js +13 -0
- package/dist/registry/asset-storage/resolve.d.ts +45 -0
- package/dist/registry/asset-storage/resolve.js +36 -0
- package/dist/registry/asset-storage/types.d.ts +51 -0
- package/dist/registry/asset-storage/types.js +9 -0
- package/dist/registry/assets/layout.d.ts +22 -0
- package/dist/registry/assets/layout.js +30 -0
- package/dist/registry/assets/sets.d.ts +18 -0
- package/dist/registry/assets/sets.js +44 -0
- package/dist/registry/assets/transfer.d.ts +45 -0
- package/dist/registry/assets/transfer.js +11 -0
- package/dist/registry/checkout/assets.d.ts +16 -0
- package/dist/registry/checkout/assets.js +150 -0
- package/dist/registry/checkout/index.d.ts +33 -0
- package/dist/registry/checkout/index.js +610 -0
- package/dist/registry/checkout/tokens.d.ts +15 -0
- package/dist/registry/checkout/tokens.js +171 -0
- package/dist/registry/client.d.ts +61 -0
- package/dist/registry/client.js +211 -0
- package/dist/registry/connection.d.ts +30 -0
- package/dist/registry/connection.js +36 -0
- package/dist/registry/db/adapter.d.ts +43 -0
- package/dist/registry/db/adapter.js +59 -0
- package/dist/registry/db/client.d.ts +35 -0
- package/dist/registry/db/client.js +94 -0
- package/dist/registry/db/index.d.ts +5 -0
- package/dist/registry/db/index.js +24 -0
- package/dist/registry/db/migrate.d.ts +23 -0
- package/dist/registry/db/migrate.js +64 -0
- package/dist/registry/db/schema.d.ts +4869 -0
- package/dist/registry/db/schema.js +347 -0
- package/dist/registry/path.d.ts +4 -0
- package/dist/registry/path.js +31 -0
- package/dist/registry/publish/artifacts.d.ts +16 -0
- package/dist/registry/publish/artifacts.js +132 -0
- package/dist/registry/publish/assets.d.ts +16 -0
- package/dist/registry/publish/assets.js +129 -0
- package/dist/registry/publish/errors.d.ts +5 -0
- package/dist/registry/publish/errors.js +26 -0
- package/dist/registry/publish/index.d.ts +27 -0
- package/dist/registry/publish/index.js +126 -0
- package/dist/registry/publish/package.d.ts +27 -0
- package/dist/registry/publish/package.js +257 -0
- package/dist/registry/publish/publish-build.d.ts +12 -0
- package/dist/registry/publish/publish-build.js +43 -0
- package/dist/registry/publish/tokens.d.ts +18 -0
- package/dist/registry/publish/tokens.js +112 -0
- package/dist/registry/tokens/sets.d.ts +59 -0
- package/dist/registry/tokens/sets.js +109 -0
- package/dist/registry/tokens/transfer.d.ts +48 -0
- package/dist/registry/tokens/transfer.js +10 -0
- package/dist/registry/transfer.d.ts +75 -0
- package/dist/registry/transfer.js +11 -0
- package/dist/store/filesystem-assets.d.ts +17 -0
- package/dist/store/filesystem-assets.js +147 -0
- package/dist/store/filesystem.d.ts +76 -0
- package/dist/store/filesystem.js +208 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.js +38 -0
- package/dist/store/registry.d.ts +77 -0
- package/dist/store/registry.js +350 -0
- package/dist/store/source-files.d.ts +24 -0
- package/dist/store/source-files.js +147 -0
- package/dist/store/types.d.ts +185 -0
- package/dist/store/types.js +12 -0
- package/dist/transformers/plugins/csf-render.js +17 -8
- package/dist/transformers/plugins/handlebars-previews.js +10 -3
- package/dist/transformers/plugins/ssr-render.js +116 -49
- package/dist/transformers/preview/component/api.js +3 -1
- package/dist/transformers/preview/component/artifacts.js +8 -1
- package/dist/transformers/preview/component/builder.js +13 -5
- package/dist/transformers/preview/component/css.js +9 -1
- package/dist/transformers/preview/component/javascript.d.ts +9 -3
- package/dist/transformers/preview/component/javascript.js +45 -17
- package/dist/transformers/preview/component/shared-artifacts.d.ts +83 -0
- package/dist/transformers/preview/component/shared-artifacts.js +92 -0
- package/dist/transformers/preview/component.js +5 -2
- package/dist/transformers/preview/pattern/builder.js +28 -9
- package/dist/transformers/preview/pattern/html.d.ts +13 -10
- package/dist/transformers/preview/pattern/html.js +62 -27
- package/dist/transformers/preview/types.d.ts +38 -0
- package/dist/transformers/utils/handlebars.d.ts +3 -0
- package/dist/transformers/utils/handlebars.js +19 -5
- package/dist/types/config.d.ts +118 -25
- package/dist/utils/menu-shell.d.ts +60 -0
- package/dist/utils/menu-shell.js +221 -0
- package/dist/utils/stable-stringify.d.ts +2 -0
- package/dist/utils/stable-stringify.js +8 -0
- package/docs/cli.md +4 -2
- package/drizzle/0000_init_registry_storage.sql +95 -0
- package/drizzle/0001_add_review_metadata.sql +2 -0
- package/drizzle/0002_add_pages.sql +28 -0
- package/drizzle/0003_add_tokens.sql +29 -0
- package/drizzle/0004_add_assets.sql +37 -0
- package/drizzle/meta/0000_snapshot.json +642 -0
- package/drizzle/meta/0001_snapshot.json +654 -0
- package/drizzle/meta/0002_snapshot.json +835 -0
- package/drizzle/meta/0003_snapshot.json +1023 -0
- package/drizzle/meta/0004_snapshot.json +1252 -0
- package/drizzle/meta/_journal.json +41 -0
- package/drizzle.config.ts +16 -0
- package/package.json +10 -1
- package/src/app/components/Component/ComponentLists.tsx +102 -102
- package/src/app/components/Component/Preview.tsx +3 -2
- package/src/app/components/Component/RegistryActions.tsx +150 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +5 -3
- package/src/app/components/Layout/Header.tsx +21 -3
- package/src/app/components/Layout/Main.tsx +11 -3
- package/src/app/components/Navigation/MainNav.tsx +4 -2
- package/src/app/components/Navigation/MobileNav.tsx +4 -2
- package/src/app/components/Navigation/SideNav.tsx +46 -11
- package/src/app/components/NotFound.tsx +24 -0
- package/src/app/components/context/NavProvider.tsx +71 -0
- package/src/app/components/old/Header.tsx +3 -3
- package/src/app/components/util/index.ts +203 -380
- package/src/app/components/util/useCollectionAssets.ts +125 -0
- package/src/app/components/util/useFoundationTokens.ts +67 -0
- package/src/app/generated/default-pages.json +1 -0
- package/src/app/generated/nav-shell.json +1 -0
- package/src/app/lib/api/query.ts +6 -0
- package/src/app/lib/api/redact.ts +46 -0
- package/src/app/lib/asset-storage.ts +81 -0
- package/src/app/lib/docs-api/artifacts.ts +116 -0
- package/src/app/lib/docs-api/backend.ts +151 -0
- package/src/app/lib/docs-api/errors.ts +52 -0
- package/src/app/lib/docs-api/handler.ts +34 -0
- package/src/app/lib/docs-api/index.ts +7 -0
- package/src/app/lib/docs-api/records.ts +189 -0
- package/src/app/lib/docs-api/registry.ts +130 -0
- package/src/app/lib/docs-api/runtime-config.ts +173 -0
- package/src/app/lib/docs-api/serve.ts +37 -0
- package/src/app/lib/docs-api/token-detail.ts +19 -0
- package/src/app/lib/registry-api/allowlist.ts +163 -0
- package/src/app/lib/registry-api/asset-transfer.ts +363 -0
- package/src/app/lib/registry-api/auth.ts +68 -0
- package/src/app/lib/registry-api/entity-routes.ts +197 -0
- package/src/app/lib/registry-api/errors.ts +51 -0
- package/src/app/lib/registry-api/files.ts +135 -0
- package/src/app/lib/registry-api/handler.ts +96 -0
- package/src/app/lib/registry-api/index.ts +4 -0
- package/src/app/lib/registry-api/meta.ts +87 -0
- package/src/app/lib/registry-api/revalidate.ts +36 -0
- package/src/app/lib/registry-api/store.ts +348 -0
- package/src/app/lib/registry-api/token-transfer.ts +231 -0
- package/src/app/lib/registry-api/transfer.ts +513 -0
- package/src/app/lib/registry-api/validation.ts +74 -0
- package/src/app/lib/registry-connection.ts +58 -0
- package/src/app/lib/utils.ts +14 -0
- package/src/app/next.config.mjs +72 -13
- package/src/app/pages/[...slug]/index.tsx +74 -20
- package/src/app/pages/_app.tsx +5 -2
- package/src/app/pages/api/docs/artifacts/[...artifactPath].ts +16 -0
- package/src/app/pages/api/docs/assets/[collection]/[...path].ts +50 -0
- package/src/app/pages/api/docs/assets/[collection]/index.ts +22 -0
- package/src/app/pages/api/docs/components/[id]/assets/[asset].ts +33 -0
- package/src/app/pages/api/docs/components/[id]/inspect/[previewId].ts +20 -0
- package/src/app/pages/api/docs/components/[id]/previews/[previewId].ts +21 -0
- package/src/app/pages/api/docs/components/[id].ts +24 -0
- package/src/app/pages/api/docs/components.json.ts +12 -0
- package/src/app/pages/api/docs/nav.json.ts +48 -0
- package/src/app/pages/api/docs/patterns/[id]/preview.ts +19 -0
- package/src/app/pages/api/docs/patterns/[id].ts +24 -0
- package/src/app/pages/api/docs/patterns.json.ts +12 -0
- package/src/app/pages/api/docs/tokens/components/[id].ts +30 -0
- package/src/app/pages/api/docs/tokens/foundations/[type].ts +32 -0
- package/src/app/pages/api/registry/components/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/components/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/components/[id].ts +20 -0
- package/src/app/pages/api/registry/components/index.ts +19 -0
- package/src/app/pages/api/registry/health.ts +20 -0
- package/src/app/pages/api/registry/patterns/[id]/files/[...filePath].ts +19 -0
- package/src/app/pages/api/registry/patterns/[id]/files/index.ts +19 -0
- package/src/app/pages/api/registry/patterns/[id].ts +20 -0
- package/src/app/pages/api/registry/patterns/index.ts +19 -0
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +39 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +24 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +23 -0
- package/src/app/pages/api/registry/transfer/assets/blobs/have.ts +11 -0
- package/src/app/pages/api/registry/transfer/assets/index.ts +10 -0
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +26 -0
- package/src/app/pages/api/registry/transfer/tokens/index.ts +11 -0
- package/src/app/pages/assets/fonts.tsx +41 -17
- package/src/app/pages/assets/index.tsx +2 -2
- package/src/app/pages/assets/logos.tsx +2 -2
- package/src/app/pages/foundations/colors.tsx +18 -8
- package/src/app/pages/foundations/effects.tsx +18 -8
- package/src/app/pages/foundations/icons/[name]/index.tsx +26 -14
- package/src/app/pages/foundations/icons/index.tsx +13 -9
- package/src/app/pages/foundations/index.tsx +1 -1
- package/src/app/pages/foundations/logo.tsx +7 -4
- package/src/app/pages/foundations/typography.tsx +19 -9
- package/src/app/pages/index.tsx +1 -1
- package/src/app/pages/system/component/[component]/index.tsx +87 -17
- package/src/app/pages/system/index.tsx +13 -4
- package/src/app/pages/system/pattern/[pattern]/index.tsx +70 -14
- package/src/app/pages/system/tokens/components/[component]/index.tsx +42 -14
- package/src/app/pages/system/tokens/foundations/colors.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/effects.tsx +7 -5
- package/src/app/pages/system/tokens/foundations/typography.tsx +8 -4
- package/src/app/tsconfig.json +2 -3
- package/src/app-builder/build.ts +555 -11
- package/src/app-builder/client-config.ts +61 -4
- package/src/app-builder/config-diff/snapshot.ts +1 -11
- package/src/app-builder/index.ts +1 -1
- package/src/app-builder/nav-shell.ts +74 -0
- package/src/app-builder/static-export.ts +215 -0
- package/src/app-builder/vercel-output.ts +297 -0
- package/src/app-builder/watchers/utils.ts +2 -0
- package/src/app-builder/watchers.ts +149 -25
- package/src/app-builder/websocket.ts +2 -2
- package/src/artifacts/README.md +14 -0
- package/src/artifacts/index.ts +17 -0
- package/src/artifacts/types.ts +146 -0
- package/src/artifacts/url.ts +90 -0
- package/src/cache/build-cache.ts +47 -2
- package/src/commands/build/app.ts +1 -2
- package/src/commands/build/index.ts +61 -0
- package/src/commands/checkout/index.ts +66 -0
- package/src/commands/db/migrate.ts +25 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/publish/index.ts +63 -0
- package/src/config/defaults.ts +15 -0
- package/src/config/loader.ts +10 -1
- package/src/config/normalizers/declaration.ts +3 -0
- package/src/config/normalizers/page.ts +45 -0
- package/src/config/normalizers/pattern.ts +4 -1
- package/src/config/runtime.ts +48 -2
- package/src/index.ts +126 -3
- package/src/nav/index.ts +94 -0
- package/src/nav/registry.ts +54 -0
- package/src/nav/resolver.ts +217 -0
- package/src/nav/workspace.ts +87 -0
- package/src/pipeline/styles.ts +4 -35
- package/src/pipeline/token-transformers.ts +97 -0
- package/src/registry/asset-storage/adapters/vercel-blob.ts +51 -0
- package/src/registry/asset-storage/define.ts +15 -0
- package/src/registry/asset-storage/resolve.ts +72 -0
- package/src/registry/asset-storage/types.ts +46 -0
- package/src/registry/assets/layout.ts +38 -0
- package/src/registry/assets/sets.ts +48 -0
- package/src/registry/assets/transfer.ts +50 -0
- package/src/registry/checkout/assets.ts +126 -0
- package/src/registry/checkout/index.ts +596 -0
- package/src/registry/checkout/tokens.ts +152 -0
- package/src/registry/client.ts +234 -0
- package/src/registry/connection.ts +50 -0
- package/src/registry/db/adapter.ts +74 -0
- package/src/registry/db/client.ts +88 -0
- package/src/registry/db/index.ts +35 -0
- package/src/registry/db/migrate.ts +56 -0
- package/src/registry/db/schema.ts +444 -0
- package/src/registry/path.ts +26 -0
- package/src/registry/publish/artifacts.ts +147 -0
- package/src/registry/publish/assets.ts +129 -0
- package/src/registry/publish/errors.ts +34 -0
- package/src/registry/publish/index.ts +128 -0
- package/src/registry/publish/package.ts +272 -0
- package/src/registry/publish/publish-build.ts +51 -0
- package/src/registry/publish/tokens.ts +119 -0
- package/src/registry/tokens/sets.ts +132 -0
- package/src/registry/tokens/transfer.ts +53 -0
- package/src/registry/transfer.ts +82 -0
- package/src/store/filesystem-assets.ts +152 -0
- package/src/store/filesystem.ts +237 -0
- package/src/store/index.ts +62 -0
- package/src/store/registry.ts +334 -0
- package/src/store/source-files.ts +155 -0
- package/src/store/types.ts +210 -0
- package/src/transformers/plugins/csf-render.ts +31 -7
- package/src/transformers/plugins/handlebars-previews.ts +22 -3
- package/src/transformers/plugins/ssr-render.ts +145 -57
- package/src/transformers/preview/component/api.ts +2 -1
- package/src/transformers/preview/component/artifacts.ts +10 -1
- package/src/transformers/preview/component/builder.ts +9 -2
- package/src/transformers/preview/component/css.ts +10 -1
- package/src/transformers/preview/component/javascript.ts +55 -20
- package/src/transformers/preview/component/shared-artifacts.ts +146 -0
- package/src/transformers/preview/component.ts +5 -2
- package/src/transformers/preview/pattern/builder.ts +33 -7
- package/src/transformers/preview/pattern/html.ts +71 -27
- package/src/transformers/preview/types.ts +42 -0
- package/src/transformers/utils/handlebars.ts +31 -5
- package/src/types/config.ts +123 -25
- package/src/utils/menu-shell.ts +241 -0
- package/src/utils/stable-stringify.ts +7 -0
- package/templates/default/package.json.tpl +1 -1
- package/handoff.config.js +0 -90
- package/src/app/components/ComponentSearch.tsx +0 -93
|
@@ -9,10 +9,12 @@ import Footer from '../../../../components/Footer';
|
|
|
9
9
|
import Layout from '../../../../components/Layout/Main';
|
|
10
10
|
import HeadersType from '../../../../components/Typography/Headers';
|
|
11
11
|
import { buttonVariants } from '../../../../components/ui/button';
|
|
12
|
-
import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens } from '../../../../components/util';
|
|
12
|
+
import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig, getTokens, isRegistryRuntime } from '../../../../components/util';
|
|
13
|
+
import { useSingleAsset, type DisplayableAsset } from '../../../../components/util/useCollectionAssets';
|
|
13
14
|
|
|
14
|
-
const DisplayIcon: React.FC<{ icon:
|
|
15
|
+
const DisplayIcon: React.FC<{ icon: DisplayableAsset }> = ({ icon }) => {
|
|
15
16
|
const htmlData = React.useMemo(() => {
|
|
17
|
+
if (!icon.data) return '';
|
|
16
18
|
// For SSR
|
|
17
19
|
if (typeof window === 'undefined') {
|
|
18
20
|
return icon.data.replace('<svg', '<svg class="o-icon"');
|
|
@@ -30,19 +32,28 @@ const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }) => {
|
|
|
30
32
|
return svgElement.outerHTML;
|
|
31
33
|
}, [icon.data]);
|
|
32
34
|
|
|
35
|
+
// Registry: no inline body yet, so render the individual asset content URL.
|
|
36
|
+
if (!icon.data) {
|
|
37
|
+
return <img className="o-icon" src={icon.src} alt={icon.name} />;
|
|
38
|
+
}
|
|
33
39
|
return <>{HtmlReactParser(htmlData)}</>;
|
|
34
40
|
};
|
|
35
41
|
|
|
36
42
|
/**
|
|
37
|
-
* Render all index pages
|
|
43
|
+
* Render all index pages. In registry mode the icon set is unknown at build time (nothing is baked),
|
|
44
|
+
* so paths are resolved on demand via `fallback: 'blocking'` and the page hydrates from the API.
|
|
38
45
|
* @returns
|
|
39
46
|
*/
|
|
40
47
|
export async function getStaticPaths() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
if (isRegistryRuntime()) {
|
|
49
|
+
return { paths: [], fallback: 'blocking' as const };
|
|
50
|
+
}
|
|
51
|
+
const paths =
|
|
52
|
+
getTokens().assets?.icons?.map((icon) => ({
|
|
53
|
+
params: {
|
|
54
|
+
name: icon.name,
|
|
55
|
+
},
|
|
56
|
+
})) ?? [];
|
|
46
57
|
|
|
47
58
|
return {
|
|
48
59
|
paths,
|
|
@@ -58,12 +69,12 @@ export async function getStaticPaths() {
|
|
|
58
69
|
* @param context GetStaticProps
|
|
59
70
|
* @returns
|
|
60
71
|
*/
|
|
61
|
-
export const getStaticProps: GetStaticProps = () => {
|
|
72
|
+
export const getStaticProps: GetStaticProps = async () => {
|
|
62
73
|
return {
|
|
63
74
|
props: {
|
|
64
|
-
...fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`).props,
|
|
75
|
+
...(await fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`)).props,
|
|
65
76
|
config: getClientRuntimeConfig(),
|
|
66
|
-
assets:
|
|
77
|
+
assets: buildTimeAssets(),
|
|
67
78
|
},
|
|
68
79
|
};
|
|
69
80
|
};
|
|
@@ -73,12 +84,13 @@ export default function SingleIcon({ menu, metadata, current, config, assets }:
|
|
|
73
84
|
const nameParam = router.query.name;
|
|
74
85
|
let name = undefined;
|
|
75
86
|
if (typeof nameParam === 'string') { name = nameParam } else if (Array.isArray(nameParam)) { name = nameParam[0] } else { name = undefined }
|
|
76
|
-
|
|
87
|
+
// Workspace/static uses the baked asset; registry hydrates the single asset (+ its SVG body) by id.
|
|
88
|
+
const icon = useSingleAsset('icons', name, assets?.icons?.find((i) => i.icon === name));
|
|
77
89
|
|
|
78
90
|
const copySvg = React.useCallback<React.MouseEventHandler>(
|
|
79
91
|
(event) => {
|
|
80
92
|
event.preventDefault();
|
|
81
|
-
if (icon) {
|
|
93
|
+
if (icon?.data) {
|
|
82
94
|
navigator.clipboard.writeText(icon.data);
|
|
83
95
|
}
|
|
84
96
|
},
|
|
@@ -113,7 +125,7 @@ export default function SingleIcon({ menu, metadata, current, config, assets }:
|
|
|
113
125
|
</Link>
|
|
114
126
|
|
|
115
127
|
<Link
|
|
116
|
-
href={'data:text/plain;charset=utf-8,' + encodeURIComponent(icon.data)}
|
|
128
|
+
href={icon.src ?? 'data:text/plain;charset=utf-8,' + encodeURIComponent(icon.data ?? '')}
|
|
117
129
|
download={icon.name}
|
|
118
130
|
className={buttonVariants({ variant: 'outline', size: 'sm' }) + ' font-normal [&_svg]:size-3!'}
|
|
119
131
|
>
|
|
@@ -12,10 +12,12 @@ import { MarkdownComponents, remarkCodeMeta } from '../../../components/Markdown
|
|
|
12
12
|
import HeadersType from '../../../components/Typography/Headers';
|
|
13
13
|
import { buttonVariants } from '../../../components/ui/button';
|
|
14
14
|
import { Input } from '../../../components/ui/input';
|
|
15
|
-
import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig
|
|
15
|
+
import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../../components/util';
|
|
16
|
+
import { useCollectionAssets, type DisplayableAsset } from '../../../components/util/useCollectionAssets';
|
|
16
17
|
|
|
17
|
-
export const DisplayIcon: React.FC<{ icon:
|
|
18
|
+
export const DisplayIcon: React.FC<{ icon: DisplayableAsset }> = ({ icon }) => {
|
|
18
19
|
const htmlData = React.useMemo(() => {
|
|
20
|
+
if (!icon.data) return '';
|
|
19
21
|
// For SSR
|
|
20
22
|
if (typeof window === 'undefined') {
|
|
21
23
|
return icon.data.replace('<svg', '<svg class="o-icon"');
|
|
@@ -35,7 +37,8 @@ export const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }
|
|
|
35
37
|
<div className="flex flex-col gap-2">
|
|
36
38
|
<Link href={`/foundations/icons/${icon.icon}`}>
|
|
37
39
|
<div className="flex flex-col items-center gap-6 rounded-lg border border-gray-100/80 bg-gray-100/80 py-12 transition-all hover:border-gray-300 hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-gray-700 dark:hover:bg-gray-900">
|
|
38
|
-
|
|
40
|
+
{/* Workspace/static: inline SVG body. Registry: the individual asset content URL. */}
|
|
41
|
+
{icon.data ? <div dangerouslySetInnerHTML={{ __html: htmlData }} /> : <img className="o-icon h-6 w-6" src={icon.src} alt={icon.name} />}
|
|
39
42
|
</div>
|
|
40
43
|
</Link>
|
|
41
44
|
<Link href={`/foundations/icons/${icon.icon}`}>
|
|
@@ -55,9 +58,9 @@ export const DisplayIcon: React.FC<{ icon: CoreTypes.IAssetObject }> = ({ icon }
|
|
|
55
58
|
export const getStaticProps: GetStaticProps = async () => {
|
|
56
59
|
return {
|
|
57
60
|
props: {
|
|
58
|
-
...fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`).props,
|
|
61
|
+
...(await fetchDocPageMarkdown('docs/foundations/', 'icons', `/foundations`)).props,
|
|
59
62
|
config: getClientRuntimeConfig(),
|
|
60
|
-
assets:
|
|
63
|
+
assets: buildTimeAssets(),
|
|
61
64
|
},
|
|
62
65
|
};
|
|
63
66
|
};
|
|
@@ -66,11 +69,12 @@ const IconsPage = ({ content, menu, metadata, current, config, assets }: AssetDo
|
|
|
66
69
|
const [search, setSearch] = React.useState('');
|
|
67
70
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
68
71
|
|
|
72
|
+
// Workspace/static hydrates from build-time props; registry hydrates from the docs read API.
|
|
73
|
+
const allIcons = useCollectionAssets('icons', assets?.icons);
|
|
69
74
|
const icons = React.useMemo(() => {
|
|
70
|
-
if (!
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}, [assets, search]);
|
|
75
|
+
if (!search) return allIcons;
|
|
76
|
+
return allIcons.filter((icon) => icon.index.includes(search));
|
|
77
|
+
}, [allIcons, search]);
|
|
74
78
|
|
|
75
79
|
const filterList = React.useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
|
|
76
80
|
setSearch(event.currentTarget.value.toLowerCase().replace(/[\W_]+/g, ' '));
|
|
@@ -20,7 +20,7 @@ import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from
|
|
|
20
20
|
export const getStaticProps: GetStaticProps = async () => {
|
|
21
21
|
return {
|
|
22
22
|
props: {
|
|
23
|
-
...fetchDocPageMarkdown('docs/', 'foundations', `/foundations`).props,
|
|
23
|
+
...(await fetchDocPageMarkdown('docs/', 'foundations', `/foundations`)).props,
|
|
24
24
|
config: getClientRuntimeConfig(),
|
|
25
25
|
},
|
|
26
26
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { GetStaticProps } from 'next';
|
|
2
2
|
import Link from 'next/link';
|
|
3
3
|
|
|
4
|
-
import { AssetDocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig
|
|
4
|
+
import { AssetDocumentationProps, buildTimeAssets, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../components/util';
|
|
5
|
+
import { useCollectionAssets } from '../../components/util/useCollectionAssets';
|
|
5
6
|
|
|
6
7
|
import { Download } from 'lucide-react';
|
|
7
8
|
import ReactMarkdown from 'react-markdown';
|
|
@@ -39,14 +40,16 @@ import { buttonVariants } from '../../components/ui/button';
|
|
|
39
40
|
export const getStaticProps: GetStaticProps = async () => {
|
|
40
41
|
return {
|
|
41
42
|
props: {
|
|
42
|
-
...fetchDocPageMarkdown('docs/foundations/', 'logo', `/foundations`).props,
|
|
43
|
+
...(await fetchDocPageMarkdown('docs/foundations/', 'logo', `/foundations`)).props,
|
|
43
44
|
config: getClientRuntimeConfig(),
|
|
44
|
-
assets:
|
|
45
|
+
assets: buildTimeAssets(),
|
|
45
46
|
},
|
|
46
47
|
};
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const LogoPage = ({ content, menu, metadata, current, config, assets }: AssetDocumentationProps) => {
|
|
51
|
+
// Workspace/static uses build-time props; registry hydrates from the docs read API.
|
|
52
|
+
const logos = useCollectionAssets('logos', assets?.logos);
|
|
50
53
|
return (
|
|
51
54
|
<Layout config={config} menu={menu} metadata={metadata} current={current}>
|
|
52
55
|
<div className="flex flex-col gap-2 pb-7">
|
|
@@ -67,7 +70,7 @@ const LogoPage = ({ content, menu, metadata, current, config, assets }: AssetDoc
|
|
|
67
70
|
<p className="mb-8">There is one main {config?.app?.client} logo that supports two variations.</p>
|
|
68
71
|
</div>
|
|
69
72
|
<div className="mb-8 grid grid-cols-2 gap-6">
|
|
70
|
-
{
|
|
73
|
+
{logos.map((logo) => (
|
|
71
74
|
<DisplayLogo logo={logo} content={config?.app?.client} key={logo.path} />
|
|
72
75
|
))}
|
|
73
76
|
</div>
|
|
@@ -10,7 +10,8 @@ import Layout from '../../components/Layout/Main';
|
|
|
10
10
|
import { MarkdownComponents, remarkCodeMeta } from '../../components/Markdown/MarkdownComponents';
|
|
11
11
|
import AnchorNav, { anchorSlugify } from '../../components/Navigation/AnchorNav';
|
|
12
12
|
import HeadersType from '../../components/Typography/Headers';
|
|
13
|
-
import { fetchFoundationDocPageMarkdown, FoundationDocumentationProps, getClientRuntimeConfig, getTokens } from '../../components/util';
|
|
13
|
+
import { fetchFoundationDocPageMarkdown, FoundationDocumentationProps, getClientRuntimeConfig, getTokens, buildTimeFoundationDesign } from '../../components/util';
|
|
14
|
+
import { useFoundationTokens } from '../../components/util/useFoundationTokens';
|
|
14
15
|
|
|
15
16
|
export interface typographyTypes {
|
|
16
17
|
[key: string]: any;
|
|
@@ -27,9 +28,9 @@ export interface typographyTypes {
|
|
|
27
28
|
export const getStaticProps: next.GetStaticProps = async () => {
|
|
28
29
|
return {
|
|
29
30
|
props: {
|
|
30
|
-
...fetchFoundationDocPageMarkdown('docs/foundations/', 'typography', `/foundations`).props,
|
|
31
|
+
...(await fetchFoundationDocPageMarkdown('docs/foundations/', 'typography', `/foundations`)).props,
|
|
31
32
|
config: getClientRuntimeConfig(),
|
|
32
|
-
design:
|
|
33
|
+
design: buildTimeFoundationDesign(),
|
|
33
34
|
},
|
|
34
35
|
};
|
|
35
36
|
};
|
|
@@ -39,13 +40,22 @@ const Typography = ({
|
|
|
39
40
|
menu,
|
|
40
41
|
metadata,
|
|
41
42
|
current,
|
|
42
|
-
scss,
|
|
43
|
-
css,
|
|
44
|
-
styleDictionary,
|
|
45
|
-
types,
|
|
43
|
+
scss: scssProp,
|
|
44
|
+
css: cssProp,
|
|
45
|
+
styleDictionary: styleDictionaryProp,
|
|
46
|
+
types: typesProp,
|
|
46
47
|
config,
|
|
47
|
-
design,
|
|
48
|
+
design: designProp,
|
|
48
49
|
}: FoundationDocumentationProps) => {
|
|
50
|
+
// In registry mode the build-time token data/downloads are empty; hydrate from the docs read API.
|
|
51
|
+
const { design, css, scss, styleDictionary, types } = useFoundationTokens('typography', {
|
|
52
|
+
design: designProp,
|
|
53
|
+
css: cssProp,
|
|
54
|
+
scss: scssProp,
|
|
55
|
+
styleDictionary: styleDictionaryProp,
|
|
56
|
+
types: typesProp,
|
|
57
|
+
});
|
|
58
|
+
|
|
49
59
|
const typography = design.typography.slice().sort((a, b) => {
|
|
50
60
|
const l = (config?.app?.type_sort ?? []).indexOf(a.name) >>> 0;
|
|
51
61
|
const r = (config?.app?.type_sort ?? []).indexOf(b.name) >>> 0;
|
|
@@ -57,7 +67,7 @@ const Typography = ({
|
|
|
57
67
|
...result,
|
|
58
68
|
[current.values.fontFamily]: result[current.values.fontFamily]
|
|
59
69
|
? // sorts and returns unique font weights
|
|
60
|
-
|
|
70
|
+
sortedUniq([...result[current.values.fontFamily], current.values.fontWeight].sort((a, b) => a - b))
|
|
61
71
|
: [current.values.fontWeight],
|
|
62
72
|
};
|
|
63
73
|
}, {} as FontFamily);
|
package/src/app/pages/index.tsx
CHANGED
|
@@ -23,7 +23,7 @@ import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from
|
|
|
23
23
|
export const getStaticProps: GetStaticProps = async () => {
|
|
24
24
|
return {
|
|
25
25
|
props: {
|
|
26
|
-
...fetchDocPageMarkdown('docs/', 'index', `/`).props,
|
|
26
|
+
...(await fetchDocPageMarkdown('docs/', 'index', `/`)).props,
|
|
27
27
|
config: getClientRuntimeConfig(),
|
|
28
28
|
},
|
|
29
29
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { buildArtifactUrl } from '@handoff/artifacts/url';
|
|
2
3
|
import { OptionalPreviewRender } from '@handoff/transformers/preview/types';
|
|
3
4
|
import { PreviewObject } from '@handoff/types/preview';
|
|
4
5
|
import { evaluateFilter, type Filter } from '@handoff/utils/filter';
|
|
@@ -8,6 +9,7 @@ import ReactMarkdown from 'react-markdown';
|
|
|
8
9
|
import rehypeRaw from 'rehype-raw';
|
|
9
10
|
import remarkGfm from 'remark-gfm';
|
|
10
11
|
import { ComponentPreview } from '../../../../components/Component/Preview';
|
|
12
|
+
import { RegistryActions } from '../../../../components/Component/RegistryActions';
|
|
11
13
|
import { HotReloadProvider } from '../../../../components/context/HotReloadProvider';
|
|
12
14
|
import { PreviewContextProvider } from '../../../../components/context/PreviewContext';
|
|
13
15
|
import Layout from '../../../../components/Layout/Main';
|
|
@@ -18,15 +20,21 @@ import HeadersType from '../../../../components/Typography/Headers';
|
|
|
18
20
|
import { Button } from '../../../../components/ui/button';
|
|
19
21
|
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '../../../../components/ui/drawer';
|
|
20
22
|
import { JsonTreeView } from '../../../../components/ui/json-tree-view';
|
|
21
|
-
import { fetchComponents, fetchDocPageMetadataAndContent, getClientRuntimeConfig,
|
|
23
|
+
import { fetchComponents, fetchDocPageMetadataAndContent, getClientRuntimeConfig, getNavProps, IParams, isRegistryRuntime } from '../../../../components/util';
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Render all index pages
|
|
25
27
|
* @returns
|
|
26
28
|
*/
|
|
27
29
|
export async function getStaticPaths() {
|
|
30
|
+
// Registry content is mutable at runtime and may be empty/unreachable at build time, so resolve
|
|
31
|
+
// component pages on demand against the live DB instead of freezing the path list to the build
|
|
32
|
+
// snapshot. The static-export target stays fully prerendered (`fallback: false`).
|
|
33
|
+
if (isRegistryRuntime()) {
|
|
34
|
+
return { paths: [], fallback: 'blocking' as const };
|
|
35
|
+
}
|
|
28
36
|
return {
|
|
29
|
-
paths: fetchComponents()
|
|
37
|
+
paths: (await fetchComponents()).map((exportable) => ({ params: { component: exportable.id } })),
|
|
30
38
|
fallback: false, // can also be true or 'blocking'
|
|
31
39
|
};
|
|
32
40
|
}
|
|
@@ -72,10 +80,15 @@ export const getStaticProps = async (context) => {
|
|
|
72
80
|
|
|
73
81
|
// const componentObject = getTokens().components[reduceSlugToString(component)] ?? null;
|
|
74
82
|
// const isFigmaComponent = false;
|
|
75
|
-
const components = fetchComponents()
|
|
76
|
-
const menu = staticBuildMenu();
|
|
77
|
-
const config = getClientRuntimeConfig();
|
|
83
|
+
const components = await fetchComponents();
|
|
78
84
|
const componentData = components.find((c) => c.id === component);
|
|
85
|
+
// Under `fallback: 'blocking'` this runs on demand for arbitrary ids; surface a real 404 for ids
|
|
86
|
+
// that do not exist in the active mode instead of dereferencing an undefined record below.
|
|
87
|
+
if (!componentData) {
|
|
88
|
+
return { notFound: true };
|
|
89
|
+
}
|
|
90
|
+
const navProps = await getNavProps('/system');
|
|
91
|
+
const config = getClientRuntimeConfig();
|
|
79
92
|
const docs = fetchDocPageMetadataAndContent('docs/system/', component as string);
|
|
80
93
|
const componentHotReloadIsAvailable = process.env.NODE_ENV === 'development';
|
|
81
94
|
const sameGroupComponents = components.filter((c) => c.group === componentData?.group);
|
|
@@ -83,7 +96,7 @@ export const getStaticProps = async (context) => {
|
|
|
83
96
|
const previousComponent = sameGroupComponents[groupIndex - 1] ?? null;
|
|
84
97
|
const nextComponent = sameGroupComponents[groupIndex + 1] ?? null;
|
|
85
98
|
|
|
86
|
-
const fallbackTitle = componentData.
|
|
99
|
+
const fallbackTitle = componentData.title || startCase(component as string);
|
|
87
100
|
const fallbackMetaTitle = `${fallbackTitle}${config?.app?.client ? ` | ${config.app.client} Design System` : ''}`;
|
|
88
101
|
|
|
89
102
|
return {
|
|
@@ -91,12 +104,11 @@ export const getStaticProps = async (context) => {
|
|
|
91
104
|
id: component,
|
|
92
105
|
// isFigmaComponent: true,
|
|
93
106
|
previews: { components: {} },
|
|
94
|
-
|
|
107
|
+
...navProps,
|
|
95
108
|
config,
|
|
96
|
-
current: getCurrentSection(menu, '/system') ?? [],
|
|
97
109
|
metadata: {
|
|
98
110
|
...componentData,
|
|
99
|
-
title: componentData.
|
|
111
|
+
title: componentData.title || docs.metadata.title || startCase(component as string),
|
|
100
112
|
description: componentData.description,
|
|
101
113
|
metaTitle: docs.metadata.metaTitle || fallbackMetaTitle,
|
|
102
114
|
metaDescription: docs.metadata.metaDescription || componentData.description,
|
|
@@ -117,28 +129,46 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
|
|
|
117
129
|
const [component, setComponent] = useState<PreviewObject>(undefined);
|
|
118
130
|
const ref = React.useRef<HTMLDivElement>(null);
|
|
119
131
|
const [componentPreviews, setComponentPreviews] = useState<PreviewObject | [string, PreviewObject][]>();
|
|
132
|
+
const [previewsUnavailable, setPreviewsUnavailable] = useState(false);
|
|
120
133
|
|
|
121
134
|
const appBasePath = process.env.HANDOFF_APP_BASE_PATH ?? '';
|
|
122
135
|
const normalizedBasePath = appBasePath ? `/${appBasePath.replace(/^\/+|\/+$/g, '')}` : '';
|
|
123
136
|
const componentRoute = (componentId: string) => `${normalizedBasePath}/system/component/${componentId}`;
|
|
124
137
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
const componentDataUrl = buildArtifactUrl(`component/${id}.json`, appBasePath);
|
|
139
|
+
|
|
140
|
+
const fetchComponents = async (signal: AbortSignal) => {
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetch(componentDataUrl, { signal });
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
setPreviewsUnavailable(true);
|
|
145
|
+
setComponent(undefined);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const data = await res.json();
|
|
149
|
+
setComponent(data as PreviewObject);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// A superseded fetch (the user navigated to another component before this one resolved) is
|
|
152
|
+
// aborted on effect cleanup; ignore it so a slow response can't overwrite the current one.
|
|
153
|
+
if ((err as Error)?.name !== 'AbortError') throw err;
|
|
154
|
+
}
|
|
128
155
|
};
|
|
129
156
|
|
|
130
157
|
const previousLink = previousComponent ? {
|
|
131
158
|
href: previousComponent ? componentRoute(previousComponent.id) : null,
|
|
132
|
-
title: previousComponent ? previousComponent.
|
|
159
|
+
title: previousComponent ? previousComponent.title : null,
|
|
133
160
|
} : null;
|
|
134
161
|
const nextLink = nextComponent ? {
|
|
135
162
|
href: componentRoute(nextComponent.id),
|
|
136
|
-
title: nextComponent.
|
|
163
|
+
title: nextComponent.title,
|
|
137
164
|
} : null;
|
|
138
165
|
|
|
139
166
|
useEffect(() => {
|
|
167
|
+
const controller = new AbortController();
|
|
140
168
|
setComponent(undefined);
|
|
141
|
-
|
|
169
|
+
setPreviewsUnavailable(false);
|
|
170
|
+
fetchComponents(controller.signal);
|
|
171
|
+
return () => controller.abort();
|
|
142
172
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
143
173
|
}, [id]);
|
|
144
174
|
|
|
@@ -163,8 +193,47 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
|
|
|
163
193
|
}
|
|
164
194
|
}, [component, id]);
|
|
165
195
|
|
|
166
|
-
if (
|
|
167
|
-
|
|
196
|
+
if (previewsUnavailable) {
|
|
197
|
+
return (
|
|
198
|
+
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
199
|
+
<div className="flex flex-col gap-3 pb-14">
|
|
200
|
+
<small className="text-sm font-medium text-sky-600 dark:text-gray-300">Components</small>
|
|
201
|
+
<HeadersType.H1>{metadata.title}</HeadersType.H1>
|
|
202
|
+
{metadata.description && (
|
|
203
|
+
<div className="prose max-w-[800px] text-xl font-light leading-relaxed text-gray-600 dark:text-gray-300">
|
|
204
|
+
<ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
|
|
205
|
+
{metadata.description}
|
|
206
|
+
</ReactMarkdown>
|
|
207
|
+
</div>
|
|
208
|
+
)}
|
|
209
|
+
<div className="mt-4 max-w-[800px] rounded-md border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800 dark:border-amber-900/50 dark:bg-amber-950/40 dark:text-amber-200">
|
|
210
|
+
This component has no published artifacts yet, so previews and code samples are unavailable. Publish it to the
|
|
211
|
+
registry to populate its documentation.
|
|
212
|
+
</div>
|
|
213
|
+
<hr className="mt-8" />
|
|
214
|
+
<PrevNextNav previous={previousLink} next={nextLink} />
|
|
215
|
+
</div>
|
|
216
|
+
</Layout>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (!component) {
|
|
220
|
+
return (
|
|
221
|
+
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
222
|
+
<div className="flex flex-col gap-3 pb-14" aria-busy="true" aria-live="polite">
|
|
223
|
+
<small className="text-sm font-medium text-sky-600 dark:text-gray-300">Components</small>
|
|
224
|
+
<HeadersType.H1>{metadata.title}</HeadersType.H1>
|
|
225
|
+
{metadata.description && (
|
|
226
|
+
<div className="prose max-w-[800px] text-xl font-light leading-relaxed text-gray-600 dark:text-gray-300">
|
|
227
|
+
<ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
|
|
228
|
+
{metadata.description}
|
|
229
|
+
</ReactMarkdown>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
233
|
+
</Layout>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const apiUrl = (window.location.origin && window.location.origin) + componentDataUrl;
|
|
168
237
|
return (
|
|
169
238
|
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
170
239
|
<div className="flex flex-col gap-3 pb-14">
|
|
@@ -178,6 +247,7 @@ const GenericComponentPage = ({ menu, metadata, current, id, config, componentHo
|
|
|
178
247
|
</ReactMarkdown>
|
|
179
248
|
</div>
|
|
180
249
|
<div className="flex flex-row gap-3">
|
|
250
|
+
<RegistryActions mode={config.runtime.mode} connected={config.runtime.connected} entityType="component" id={id} />
|
|
181
251
|
{component.figma && (
|
|
182
252
|
<Button asChild variant={'outline'} size={'sm'} className="font-normal [&_svg]:size-3!">
|
|
183
253
|
<a href={component.figma} target="_blank">
|
|
@@ -36,7 +36,7 @@ export const getStaticProps: GetStaticProps = async () => {
|
|
|
36
36
|
return {
|
|
37
37
|
props: {
|
|
38
38
|
config,
|
|
39
|
-
...fetchDocPageMarkdown('docs/', 'system', `/system`).props,
|
|
39
|
+
...(await fetchDocPageMarkdown('docs/', 'system', `/system`)).props,
|
|
40
40
|
} as ComponentPageDocumentationProps,
|
|
41
41
|
};
|
|
42
42
|
};
|
|
@@ -49,13 +49,22 @@ const ComponentsPage = ({ content, menu, metadata, current, config }: ComponentP
|
|
|
49
49
|
// Fetch components from api
|
|
50
50
|
const [components, setComponents] = useState<PreviewObject[]>(undefined);
|
|
51
51
|
const fetchComponents = async () => {
|
|
52
|
-
const data = await fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/components.json`).then((res) => res.json());
|
|
52
|
+
const data = await fetch(`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/docs/components.json`).then((res) => res.json());
|
|
53
53
|
setComponents(data as PreviewObject[]);
|
|
54
54
|
};
|
|
55
55
|
useEffect(() => {
|
|
56
56
|
fetchComponents();
|
|
57
57
|
}, []);
|
|
58
|
-
if (!components)
|
|
58
|
+
if (!components) {
|
|
59
|
+
return (
|
|
60
|
+
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
61
|
+
<div className="flex flex-col gap-2 pb-7" aria-busy="true" aria-live="polite">
|
|
62
|
+
<HeadersType.H1>{metadata.title}</HeadersType.H1>
|
|
63
|
+
<p className="mt-3 text-lg leading-relaxed text-gray-600 dark:text-gray-300">{metadata.description}</p>
|
|
64
|
+
</div>
|
|
65
|
+
</Layout>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
59
68
|
|
|
60
69
|
if ((components ?? []).length === 0) {
|
|
61
70
|
return (
|
|
@@ -90,7 +99,7 @@ const ComponentsPage = ({ content, menu, metadata, current, config }: ComponentP
|
|
|
90
99
|
);
|
|
91
100
|
}
|
|
92
101
|
|
|
93
|
-
const apiUrl = (window.location.origin && window.location.origin) + `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/components.json`;
|
|
102
|
+
const apiUrl = (window.location.origin && window.location.origin) + `${process.env.HANDOFF_APP_BASE_PATH ?? ''}/api/docs/components.json`;
|
|
94
103
|
return (
|
|
95
104
|
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
96
105
|
<div className="flex flex-col gap-2 pb-7">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { buildArtifactUrl } from '@handoff/artifacts/url';
|
|
2
3
|
import { PatternListObject } from '@handoff/transformers/preview/types';
|
|
3
4
|
import { startCase } from 'lodash';
|
|
4
5
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
@@ -7,6 +8,7 @@ import rehypeRaw from 'rehype-raw';
|
|
|
7
8
|
import Layout from '../../../../components/Layout/Main';
|
|
8
9
|
import { MarkdownComponents } from '../../../../components/Markdown/MarkdownComponents';
|
|
9
10
|
import PrevNextNav from '../../../../components/Navigation/PrevNextNav';
|
|
11
|
+
import NotFound from '../../../../components/NotFound';
|
|
10
12
|
import HeadersType from '../../../../components/Typography/Headers';
|
|
11
13
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../../../../components/ui/accordion';
|
|
12
14
|
import { Badge } from '../../../../components/ui/badge';
|
|
@@ -15,9 +17,9 @@ import {
|
|
|
15
17
|
DocumentationProps,
|
|
16
18
|
fetchPatterns,
|
|
17
19
|
getClientRuntimeConfig,
|
|
18
|
-
|
|
20
|
+
getNavProps,
|
|
19
21
|
IParams,
|
|
20
|
-
|
|
22
|
+
isRegistryRuntime,
|
|
21
23
|
} from '../../../../components/util';
|
|
22
24
|
|
|
23
25
|
type PatternNavItem = { id: string; title: string; description: string; group: string };
|
|
@@ -89,18 +91,29 @@ const getArgSummary = (args?: Record<string, unknown>) => {
|
|
|
89
91
|
};
|
|
90
92
|
|
|
91
93
|
export async function getStaticPaths() {
|
|
94
|
+
// Registry content is mutable at runtime and may be empty/unreachable at build time, so resolve
|
|
95
|
+
// pattern pages on demand against the live DB instead of freezing the path list to the build
|
|
96
|
+
// snapshot. The static-export target stays fully prerendered (`fallback: false`).
|
|
97
|
+
if (isRegistryRuntime()) {
|
|
98
|
+
return { paths: [], fallback: 'blocking' as const };
|
|
99
|
+
}
|
|
92
100
|
return {
|
|
93
|
-
paths: fetchPatterns()
|
|
101
|
+
paths: (await fetchPatterns()).map((p) => ({ params: { pattern: p.id } })),
|
|
94
102
|
fallback: false,
|
|
95
103
|
};
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
export const getStaticProps = async (context: { params: IParams }) => {
|
|
99
107
|
const { pattern: patternId } = context.params;
|
|
100
|
-
const patterns = fetchPatterns()
|
|
101
|
-
const menu = staticBuildMenu();
|
|
102
|
-
const config = getClientRuntimeConfig();
|
|
108
|
+
const patterns = await fetchPatterns();
|
|
103
109
|
const patternData = patterns.find((p) => p.id === patternId);
|
|
110
|
+
// Under `fallback: 'blocking'` this runs on demand for arbitrary ids; surface a real 404 for ids
|
|
111
|
+
// that do not exist in the active mode.
|
|
112
|
+
if (!patternData) {
|
|
113
|
+
return { notFound: true };
|
|
114
|
+
}
|
|
115
|
+
const navProps = await getNavProps('/system');
|
|
116
|
+
const config = getClientRuntimeConfig();
|
|
104
117
|
|
|
105
118
|
const sameGroupPatterns = patterns.filter((p) => p.group === patternData?.group);
|
|
106
119
|
const groupIndex = sameGroupPatterns.findIndex((p) => p.id === patternId);
|
|
@@ -112,9 +125,8 @@ export const getStaticProps = async (context: { params: IParams }) => {
|
|
|
112
125
|
return {
|
|
113
126
|
props: {
|
|
114
127
|
id: patternId,
|
|
115
|
-
|
|
128
|
+
...navProps,
|
|
116
129
|
config,
|
|
117
|
-
current: getCurrentSection(menu, '/system') ?? [],
|
|
118
130
|
metadata: {
|
|
119
131
|
title: fallbackTitle,
|
|
120
132
|
description: patternData?.description || '',
|
|
@@ -130,6 +142,7 @@ export const getStaticProps = async (context: { params: IParams }) => {
|
|
|
130
142
|
const PatternPage = ({ menu, metadata, current, id, config, previousPattern, nextPattern }: PatternPageProps) => {
|
|
131
143
|
const [pattern, setPattern] = useState<PatternListObject | undefined>(undefined);
|
|
132
144
|
const [fetchError, setFetchError] = useState<string | undefined>(undefined);
|
|
145
|
+
const [patternNotFound, setPatternNotFound] = useState(false);
|
|
133
146
|
const [iframeHeight, setIframeHeight] = useState('400px');
|
|
134
147
|
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
|
135
148
|
|
|
@@ -145,13 +158,20 @@ const PatternPage = ({ menu, metadata, current, id, config, previousPattern, nex
|
|
|
145
158
|
const controller = new AbortController();
|
|
146
159
|
setPattern(undefined);
|
|
147
160
|
setFetchError(undefined);
|
|
161
|
+
setPatternNotFound(false);
|
|
148
162
|
|
|
149
|
-
fetch(
|
|
163
|
+
fetch(buildArtifactUrl(`pattern/${id}.json`, appBasePath), { signal: controller.signal })
|
|
150
164
|
.then((res) => {
|
|
165
|
+
if (res.status === 404) {
|
|
166
|
+
setPatternNotFound(true);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
151
169
|
if (!res.ok) throw new Error(`Failed to load pattern: ${res.status} ${res.statusText}`);
|
|
152
170
|
return res.json();
|
|
153
171
|
})
|
|
154
|
-
.then((data) =>
|
|
172
|
+
.then((data) => {
|
|
173
|
+
if (data) setPattern(data as PatternListObject);
|
|
174
|
+
})
|
|
155
175
|
.catch((err) => {
|
|
156
176
|
if (err.name !== 'AbortError') {
|
|
157
177
|
setFetchError(err instanceof Error ? err.message : 'Failed to load pattern data.');
|
|
@@ -159,7 +179,7 @@ const PatternPage = ({ menu, metadata, current, id, config, previousPattern, nex
|
|
|
159
179
|
});
|
|
160
180
|
|
|
161
181
|
return () => controller.abort();
|
|
162
|
-
}, [id,
|
|
182
|
+
}, [id, appBasePath]);
|
|
163
183
|
|
|
164
184
|
const onIframeLoad = useCallback(() => {
|
|
165
185
|
if (iframeRef.current?.contentWindow?.document?.body) {
|
|
@@ -167,8 +187,44 @@ const PatternPage = ({ menu, metadata, current, id, config, previousPattern, nex
|
|
|
167
187
|
}
|
|
168
188
|
}, []);
|
|
169
189
|
|
|
170
|
-
if (
|
|
171
|
-
|
|
190
|
+
if (patternNotFound) {
|
|
191
|
+
return (
|
|
192
|
+
<Layout config={config} menu={menu} current={current} metadata={{ ...metadata, title: '404 - Page Not Found', metaTitle: '404 - Page Not Found' }}>
|
|
193
|
+
<div className="flex min-h-[60vh] flex-col items-center justify-center">
|
|
194
|
+
<NotFound />
|
|
195
|
+
</div>
|
|
196
|
+
</Layout>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (fetchError) {
|
|
201
|
+
return (
|
|
202
|
+
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
203
|
+
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-2">
|
|
204
|
+
<p className="text-lg font-semibold text-gray-700 dark:text-gray-300">Something went wrong</p>
|
|
205
|
+
<p className="text-sm text-red-500">{fetchError}</p>
|
|
206
|
+
</div>
|
|
207
|
+
</Layout>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!pattern) {
|
|
212
|
+
return (
|
|
213
|
+
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
214
|
+
<div className="flex flex-col gap-3 pb-14" aria-busy="true" aria-live="polite">
|
|
215
|
+
<small className="text-sm font-medium text-sky-600 dark:text-gray-300">Patterns</small>
|
|
216
|
+
<HeadersType.H1>{metadata.title}</HeadersType.H1>
|
|
217
|
+
{metadata.description && (
|
|
218
|
+
<div className="prose max-w-[800px] text-xl font-light leading-relaxed text-gray-600 dark:text-gray-300">
|
|
219
|
+
<ReactMarkdown components={MarkdownComponents} rehypePlugins={[rehypeRaw]}>
|
|
220
|
+
{metadata.description}
|
|
221
|
+
</ReactMarkdown>
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
</Layout>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
172
228
|
|
|
173
229
|
return (
|
|
174
230
|
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
@@ -217,7 +273,7 @@ const PatternPage = ({ menu, metadata, current, id, config, previousPattern, nex
|
|
|
217
273
|
display: 'block',
|
|
218
274
|
border: 'none',
|
|
219
275
|
}}
|
|
220
|
-
src={
|
|
276
|
+
src={buildArtifactUrl(`pattern/${pattern.url}`, appBasePath)}
|
|
221
277
|
/>
|
|
222
278
|
) : (
|
|
223
279
|
<div className="flex items-center justify-center p-8 text-sm text-gray-500">No preview available for this pattern.</div>
|